Search code examples
javascriptjqueryajaxtwitter-bootstrapbootstrap-file-upload

Using promises to execute ajax synchronously


This is my code where I handle file uploads:

But as I am not using promises it doesn't run in proper sequence! The ajax part of the code runs at the very end when I upload multiple files (I am using krajee bootstrap and this code does the actual file upload!). Here's the code:

$('#uploadNoteImage').on('fileloaded', function (event, file, previewId, index, reader) {
    var url = noTrailingSlash(window.location.href) + '/user/notes';
    console.log("1) Notes upload number: "+index);

    var imgpgno = parseInt(getNotesLength(noteTitle, notesData));
    console.log("2) Got page number of image "+imgpgno);

    var data = {
        "subject": noteTitle,
        "pgno": imgpgno + 1,
        "note": reader.result
    };

    console.log("3) Formed data object:");
    console.log(data);
    $.ajax({
        url: url,
        method: "PUT",
        data: data,
        success: function (data) {
            console.log("4) Successfully uploaded data");
            console.log(data);
            toastr.success('', 'Added!');
            var order = getIndexToDelete(noteTitle, notesData);
            console.log("5) Fetched order number: "+order);
            var id = Math.floor(Math.random() * 1000);
            if (imgpgno == 0) {
                console.log("6)(No notes uploaded yet state) Images before uploading"+images);
                modalImg.src = reader.result;
                notesData[order].data.push({
                    "id": id, "pgno": imgpgno + 1,
                    "note": reader.result
                });
                images = imgpgno + 1;
                console.log("7)(No notes uploaded yet state) Images after uploading: "+images);
                // imgpgno++;

            }
            else if(imgpgno!=0) {
                var newPageNo=imgpgno + 1;
                console.log("6)(1 note uploaded state) Pushing data with pgno: "+newPageNo);
                notesData[order].data.push({
                    "id": id, "pgno": newPageNo,
                    "note": reader.result
                });
                images = imgpgno + 1;
                console.log("7)(1 note uploaded state) Images after uploading: "+images);
            }
        },
        error: function (err) {
            toastr.error('Try again!', 'Something went wrong in uploading note!');
            console.log(err);
        }
    });
});

How I can included promises here so that the code runs in proper sequence? I have outlined the problem here in full detail:Late execution of AJAX code


Solution

  • Simplest way requires one little "setup" -

    var uploadInProgress = $.when();
    

    this "primes" the uploadInProgress variable in such a way that the first upload will begin as soon as we want it to

    the whole code is not very different to the code you already have:

    var uploadInProgress = $.when();
    $('#uploadNoteImage').on('fileloaded', function (event, file, previewId, index, reader) {
        // here, we chain the "pending" upload to this one
        uploadInProgress = uploadInProgress.then(function() {
            var url = noTrailingSlash(window.location.href) + '/user/notes';
            console.log("1) Notes upload number: "+index);
    
            var imgpgno = parseInt(getNotesLength(noteTitle, notesData));
            console.log("2) Got page number of image "+imgpgno);
    
            var data = {
                "subject": noteTitle,
                "pgno": imgpgno + 1,
                "note": reader.result
            };
    
            console.log("3) Formed data object:");
            console.log(data);
            return $.ajax({
                url: url,
                method: "PUT",
                data: data
            // since we are using Promise pattern, use .then for success, and .catch for error conditions
            }).then(function (data) {
                console.log("4) Successfully uploaded data");
                console.log(data);
                toastr.success('', 'Added!');
                var order = getIndexToDelete(noteTitle, notesData);
                console.log("5) Fetched order number: "+order);
                var id = Math.floor(Math.random() * 1000);
                if (imgpgno == 0) {
                    console.log("6)(No notes uploaded yet state) Images before uploading"+images);
                    modalImg.src = reader.result;
                    notesData[order].data.push({
                        "id": id, "pgno": imgpgno + 1,
                        "note": reader.result
                    });
                    images = imgpgno + 1;
                    console.log("7)(No notes uploaded yet state) Images after uploading: "+images);
                    // imgpgno++;
    
                }
                else { // if(imgpgno!=0) { // the check is redundant since when the above if condition is false, this has to be true
                    var newPageNo=imgpgno + 1;
                    console.log("6)(1 note uploaded state) Pushing data with pgno: "+newPageNo);
                    notesData[order].data.push({
                        "id": id, "pgno": newPageNo,
                        "note": reader.result
                    });
                    images = imgpgno + 1;
                    console.log("7)(1 note uploaded state) Images after uploading: "+images);
                }
            }).catch(function (err) {
                // this catch ensures that the next upload will be able to run regardless of this upload's failure
                toastr.error('Try again!', 'Something went wrong in uploading note!');
                console.log(err);
            });
        });
    });
    

    Instead of the success:/error: callbacks, use the fact that $.ajax returns a Promise, and use .then/.catch - this makes it easy to chain the upload requests one after the other

    Note: make sure the .catch actually handles the error (i.e. doesn't throw one) or the promise chain will break - the code you had in error: is fine as is