1

I have lots of different functions that send AJAX requests to save different parts of the page. When the user clicks save, All these functions are run like so.

function savePage() {
    if (!confirm('Save changes?')) return false;
    saveSortOrder();
    saveAllWidth();
    saveAllTinyMCE();
    saveAllWidgetRm();

    location.reload();  
}

Once everything is saved, I want to reload the page but location.reload() runs before everything is finished.

A typical save function looks like this but some are much bigger and there are lots of them

function saveAllPublic() {
    $('.widget').each( function(){

        var parentID = $(this).attr('id');
        var publicState = $(this).attr('data-public');

        $.post('widgets/manage_widgets.php', {
            update: 'publicity',
            wd_parent: parentID,
            public: publicState
        });

    });
}

Basically, I want all the POSTs to complete before reloading the page.

Matt
  • 1,518
  • 4
  • 16
  • 30

1 Answers1

2

First of all, you will want the save functions to return an array of promises. We can do that by simply using .map(), and within in return the AJAX call you have made, i.e.:

// saveAllPublic() will return an array of promises
function saveAllPublic() {
    return $('.widget').map(function(){

        var parentID = $(this).attr('id');
        var publicState = $(this).attr('data-public');

        return $.post('widgets/manage_widgets.php', {
            update: 'publicity',
            wd_parent: parentID,
            public: publicState
        });
    }).get();
}

Note: Remember to use .get() at the end of .map(), in order to obtain a true array and not a jQuery collection (which is an array-like object). See a more thorough explanation here: map() get() confusion

When you want to check if all the requests made by saveAllPublic() is done, you can simply do this:

var saveAllPublicAJAX = saveAllPublic();
$.when.apply($, saveAllPublicAjax).then(function() {
   // Callback when all POST requests in saveAllPublic() is completed
});

And let's say based on your example you have refactored all the save functions to use the array push method I have mentioned above, you can simply concatenate all these returned arrays into a single one, and pass it to $.when:

function savePage() {
    if (!confirm('Save changes?')) return false;

    var saveAJAX = [].concat.apply([], [
        saveSortOrder(),
        saveAllWidth(),
        saveAllTinyMCE(),
        saveAllWidgetRm()
    ]);

    $.when.apply($, saveAJAX).then(function() {
        // When all the requests are successful
        location.reload();  
    }, function() {
        // When one or more requests have failed
        // ...
    });
}

There are, of course, other more verbose way of constructing the array, such as:

  • var saveAJAX = saveSortOrder().concat(saveAllWidth()).concat(...)
  • var saveAJAX = []; saveAJAX.push(saveSortOrder()); ...
Terry
  • 63,248
  • 15
  • 96
  • 118
  • @tomalak Because `[a,b,c]` will return an array of arrays, no? – Terry Aug 12 '17 at 10:24
  • @Tomalak It depends what are `a`, `b` and `c`. I have an impression that each individual function call will return an array of promises, based on my understanding of the question. Again, it's not made abundantly clear by the OP what does `saveSortOrder()` and etc does. Do they iterate through a collection of elements and make individual POST request? Or do they make singular requests? – Terry Aug 12 '17 at 10:26
  • @Tomalak What's why my original answer stands. – Terry Aug 12 '17 at 10:34
  • @Alex.W Sorry, seems like I forgot to return the array in the `saveAllPublic()` example. – Terry Aug 12 '17 at 11:23
  • One last question. How could I show a message when one of the requests fails? Currently, it just skips reload part. – Matt Aug 12 '17 at 12:13
  • @Alex.W If you want to catch a failure, simply declare a second anonymous function in `.then()`, ie `.then(function() {...}, function() {...})`. The second anonymous function will be called when one or more of the promises are rejected. – Terry Aug 12 '17 at 12:21