2

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

halfer
  • 19,824
  • 17
  • 99
  • 186
Parth
  • 2,682
  • 1
  • 20
  • 39
  • 2
    `Using promises to execute ajax synchronously` - no ... because Promises are asynchronous too - in fact, there is no way to make asynchronous code synchronous - a moments thought about it and you should realise why - and anyway ... $.ajax returns a promise already (a jquery promise, which is 99.9% Promise/A+ spec) – Jaromanda X Jun 25 '17 at 07:32
  • by the way, the code you presented will console log 1,2,3,4,5,6,7 in order - what order do you see? – Jaromanda X Jun 25 '17 at 07:37
  • the order 1 2 3 4 5 6 7 works for the first file upload! But if i upload 3 files then I get order 1 2 3 4 5 6 7 1 2 3 1 2 3 4 5 6 7 4 5 6 7. That is the order is totally messed up after first upload :(( If you click the link "Late execution of AJAX code" I have given a link to console screenshot there... Please see it?? And thanks for the prompt reply :) – Parth Jun 25 '17 at 07:45
  • well, yes, because each upload is not dependant on the others, so they will complete in different orders - did you want to ensure only one upload is "in flight" at any one time? – Jaromanda X Jun 25 '17 at 07:53
  • umm i dont understand what in flight means but i want the console sequence as 1 2 3 4 5 6 7 1 2 3 4 5 6 7 1 2 3 4 5 6 7 for 3 uploads!! i mean one after other upload with the ajax code running too..! – Parth Jun 25 '17 at 08:02
  • In flight means in the process of uploading which takes a finite amount of time. So you only want one upload happening at any one time. Why? – Jaromanda X Jun 25 '17 at 08:04
  • *Why* do you care about the upload order, is the central question here. – Tomalak Jun 25 '17 at 08:05
  • I want one upload at a time because along with upload i am locally updating some data in the ajax part and the data which will upload after current image upload needs this locally updated data to correctly create the object to upload to database! and right now as the ajax gets executed at the end the object uploaded to database contains in consistent data. In this case the value of 'imgpgno' depends on these local updates so currently 2nd and 3rd upload has same imgpgno which is wrong!! – Parth Jun 25 '17 at 08:16
  • I don't really understand. All you do is increment the counter `imgpgno`. What has that to do with the database? Is that supposed to match a database ID or something? – Tomalak Jun 25 '17 at 08:20
  • Yeah but the thing is I make an object which stores image address , its imgno and the order no(reference index) along with other stuff. Now what i want to happen is one file is uploaded , then its object which i create should be pushed locally (which happens in the ajax part ) and only after the push is complete the next file upload should happen!! – Parth Jun 25 '17 at 08:36
  • You are too hooked to that *"must happen sequentially"* idea. I get it that in your current setup things must happen sequentially. But that is because your setup is wrong, plain and simple. You *never* want sequential (or worse, synchronous) uploads. If you have multiple files to upload, you want as many parallel uploads as possible. Everything else is a technicality and you can program around that. Identify the technicalities that seem to force sequential order and solve them. That should be your focus. "Sequential uploads" is not the solution. – Tomalak Jun 25 '17 at 08:41
  • Yup I did realies this after I saw the order in which these uploads were happening... But the problem is I am not so good at web development yet and my main skills are in frontend... This back end was programmed by a friend and he is busy right now and I am trying to fix as many bugs as I can as soon as I can so we can publish! Thats why the insist on sequential upload!! :) Thanks for your time :D – Parth Jun 25 '17 at 08:48
  • @ParthTamane: Trust me. Front end programmers are much more comfortable with asynchronous code than backend programmers. Front end programmers have had years to get used to `onclick` – slebetman Jun 25 '17 at 09:32

1 Answers1

0

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

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Thanks so much!!! This is working perfectly!! I'll test it further but I don't this there are any issues here... Just I wanted to know one thing, if I remove catch part then all the uploads after one failed upload will also halt, right?? – Parth Jun 25 '17 at 08:43
  • correct, because the code works on the "resolved" chain - "handling" an error in catch by **not** throwing or returning a "rejected promise" means the chain is unbroken – Jaromanda X Jun 25 '17 at 08:57
  • Okay!! By the way I don't know why but sometimes the same image is being uploaded to the database twice or thrice! I'm going through the logs to see what is happening but so far all the mata data for each image seems correct! So is there a chance that this code picks up same image multiple times?? Because its not completely synchronous yet? I am trying to recreate the issue so i can show the log! – Parth Jun 25 '17 at 09:08
  • Okay there is still a problem! The steps run in proper sequence but more than one image is being uploaded again and again :(( Idk why! Can you please help? – Parth Jun 25 '17 at 09:40
  • okay heres what is happeneing so far: 1) First 4 image upload happened correctly. 2) After that the next in the next 4 image upload, the data did go correctly to the data base but locally first image was skipped and the last uploaded image was displayed twice 3) In the next 3 uploaded , the firt image was uploaded properly, second image was skipped and last image was stored 2 times! Any Idea why this could be happening? It's baffling me!! @JaromandaX – Parth Jun 25 '17 at 09:46