Home > Mobile >  How do I stop my code from executing until an asynchronous piece of code has completed?
How do I stop my code from executing until an asynchronous piece of code has completed?

Time:09-29

Today I've been struggling to get some JavaScript code working.

The goal of this code is to have all of the files names returned by the ajax function calls populated within the fileNameObj before the recordFormSubmission function is called.

async function uploadSubmissionFiles(files)
{
    //Get an array for the names of each key of the files object
    var filesKeys = Object.keys(files);
    var fileSubmissionErrors = [];
    var formElementPK = "";

    //Create an object that keeps track of the file names that are saved to the server
    var fileNamesObj = {};

    //For each question where we uploaded files, call an ajax function 
    //where we transfer the files from the client to the server
    var promiseToUploadFiles = () => {
        return new Promise((resolve,reject) => 
        {
            for(let i = 0; i < filesKeys.length; i  )
            {
                formElementPK = filesKeys[i].match("(?<=fpk).*");
                console.log(formElementPK);
                if(files[filesKeys[i]]["type"] == "canvas")
                {
                    var canvasElement = files[filesKeys[i]]["response"];
                    var canvasImageBase64 = canvasElement.toDataURL().split(';base64,')[1];
                    //Use ajax to upload the file to the server
                    $.ajax(
                        {
                            url: "./includes/ajaxColdfusion/fillout/submittedFileUpload.cfc?method=uploadSubmittedCanvasImage",
                            type: "POST", 
                            data: canvasImageBase64,
                            enctype: 'multipart/form-data',
                            contentType: false,
                            processData: false
                        }
                    )
                    .then( function(response)
                        {
                            var responseElements = response.getElementsByTagName("string");
                            var resultMessage = responseElements[0].innerHTML;
                            console.log("File upload result for "   canvasElement.id   ": "   resultMessage);

                            if(resultMessage == "success")
                            {
                                //On success, responseElements[1] contains the name of the file that was saved to the server. We record it!
                                fileNamesObj[formElementPK] = responseElements[1].innerHTML;
                            }
                            else
                            {
                                //On failure, responseElements[1] contains the error message for what happened during the upload
                                fileSubmissionErrors.push(responseElements[1].innerHTML);
                                fileNamesObj[formElementPK] = "error.png";
                            }
                            console.log(fileNamesObj);
                        }
                    );
                }
                else if(files[filesKeys[i]]["type"] == "fileUpload")
                {
                    var fileUploadForm = files[filesKeys[i]]["response"];
                    var formData = new FormData(fileUploadForm);
                    var fileFieldName = document.getElementById("question"   formElementPK   "FileUpload").getAttribute("name");
                    formData.append("fileFieldName", fileFieldName);
                    //Use ajax to upload the file to the server
                    $.ajax(
                        {
                            url: "./includes/ajaxColdfusion/fillout/submittedFileUpload.cfc?method=uploadSubmittedFile",
                            type: "POST", 
                            data: formData,
                            enctype: 'multipart/form-data',
                            contentType: false,
                            processData: false
                        }
                    )
                    .then( function(response)
                        {
                            var responseElements = response.getElementsByTagName("string");
                            var resultMessage = responseElements[0].innerHTML;
                            console.log("File upload result for "   fileUploadForm.id   ": "   resultMessage);

                            if(resultMessage == "success")
                            {
                                //On success, responseElements[1] contains the name of the file that was saved to the server. We record it!
                                fileNamesObj[formElementPK] = responseElements[1].innerHTML;
                            }
                            else
                            {
                                //On failure, responseElements[1] contains the error message for what happened during the upload
                                fileSubmissionErrors.push(responseElements[1].innerHTML);
                                fileNamesObj[formElementPK] = "error.png";   
                            }
                            console.log(fileNamesObj);
                        }
                    );
                }
            }
            
            var retObj = {
            "fileSubmissionErrors" : fileSubmissionErrors,
            "fileNames" : fileNamesObj
            }
            console.log("Resolved the promise!");
            resolve(retObj);
        });
    }
    
    return await promiseToUploadFiles();
}


async function recordFormSubmission(data, formPK, fileNames)
{
    var JSONData = JSON.stringify(data);
    var JSONFileNames = JSON.stringify(fileNames);
    var submissionErrors = [];

    console.log("Filenames at trigger of record form submission: ", fileNames);
    console.log("Now waiting 2 seconds with     await new Promise(r => setTimeout(r, 2000));");
    await new Promise(r => setTimeout(r, 2000));
    console.log("Fileanmes after 2 seconds of triggering record form submission", fileNames);

    $.ajax(
        {
            url: "./includes/ajaxColdfusion/fillout/recordFormSubmission.cfc?method=recordFormSubmission",
            type: "POST", 
            data: "JSONData="   JSONData   "&formPK="   formPK   "&JSONFileNames="   JSONFileNames,
            enctype: 'multipart/form-data',
            cache : false
        }
    ).
    done(function(response)
        {
            //If successful...
            console.log("Record form submission successful:"   response.getElementsByTagName("string")[0].innerHTML);
        }
    ).
    fail(function(response)
        { 
            //If not successful...
            console.log("Record form submission failed");
            submissionErrors = [response.getElementsByTagName("string")[1]];
        }
    );

    return submissionErrors;
}


uploadSubmissionFiles(filesToUpload.then( function(fromUploadSubmissionFiles)
{
     var fileSubmissionErrors = 
     fromUploadSubmissionFiles["fileSubmissionErrors"]
     var fileNames = fromUploadSubmissionFiles["fileNames"];
     //Query the database to save our form data to the database
     var formDataSubmissionErrors = recordFormSubmission(dataToRecord, formPK, fileNames);
}

Above are all the important parts of this code. As it stands right now though, recordFormSubmission() gets called and executes before the fileNamesObj variable is populated with file names that are returned from the ajax function. This must mean that the code is not waiting on the ajax to finish executing before continuing with execution, even though a promise has been implemented.

I suppose my question here is, what am I doing wrong if I want the JavaScript code to wait until all of the ajax functions are completed before calling recordFormSubmission()?

I could probably think of a polling solution here, but I'd like to think that there's a way to solve this with promises or better practice.

This is the result of running the modified code to return an awaited promise. It also shows how the ajax functions return and the filenames are obtained in the window of time between waiting two seconds and when the critical point where the filenames must exist occurs. C

CodePudding user response:

try this.

async function uploadSubmissionFiles(files)
{
     ...
     var promiseToUploadFiles = () => {
         return new Promise((resolve,reject) => 
             {
                  $.ajax(
                        {
                            url: "./includes/ajaxColdfusion/fillout/submittedFileUpload.cfc?method=uploadSubmittedFile",
                            type: "POST", 
                            data: formData,
                            enctype: 'multipart/form-data',
                            contentType: false,
                            processData: false
                        }
                    )
                    .then( function(response)
                        {
                            var responseElements = response.getElementsByTagName("string");
                            var resultMessage = responseElements[0].innerHTML;
                            console.log("File upload result for "   fileUploadForm.id   ": "   resultMessage);

                            if(resultMessage == "success")
                            {
                                //On success, responseElements[1] contains the name of the file that was saved to the server. We record it!
                                fileNamesObj[formElementPK] = responseElements[1].innerHTML;
                            }
                            else
                            {
                                //On failure, responseElements[1] contains the error message for what happened during the upload
                                fileSubmissionErrors.push(responseElements[1].innerHTML);
                                fileNamesObj[formElementPK] = "error.png";   
                            }
                            resolve(fileNamesObj);
                        }
                    )
                    .catch(function(e) {
                        reject(e)
                    });
             }
        }
    return await promiseToUploadFiles();
     ...
}

await uploadSubmissionFiles(filesToUpload).then( function(fileNameObj)
    {
        recordFormSubmission(..., fileNameObj)
    }).catch(function(e) {
        console.error(e);
    });

It is necessary to wrap your promise in a function and wait for its completion after the call. When you have assigned a promise to a simple variable, the request immediately starts working and the code runs on without waiting for completion

CodePudding user response:

The solution to this was found within an answer to another question: https://stackoverflow.com/a/66747114/16511184

Credit to @Heretic-Monkey for providing it.

I'm new to the concept of Promises in Javascript, so I unknowingly made an error of encapsulating promises inside of eachother. It's very important to note that the jquery ajax() function returns a promise. In my problem, I was triggering asynchronous code that I was not using await for. Instead I used await for a function that contained an ajax() function. The await wouldn't wait very long at all because all the code was doing was triggering the ajax() function and leaving, not waiting for it to finish (the very crux of what I wanted to occur).

The solution: put an await in front of each ajax() function call. Do NOT encapsulate anything within a promise.

  • Related