Search code examples
javascriptjqueryajaxpromise.when

jQuery .when not working as expected with rest operator


So in our code base we are using jquery just for the ajax section of the codebase but we want to wrap all of our calls so if we wanted to eventually get rid of jquery then we would only have to change the implementation. Here is the definition of the wrapper.

export const getAll = (...ajaxCalls) => {
    // return $.when($.ajax(ajaxCalls));
    return $.when(ajaxCalls.map(call => $.ajax(call)));
}

And here is the where we are calling it.

getAll(`/response/${customerId}`, `/response/${methods}`).done((response1, response2) => {
  console.log("getAll1",response1);
  console.log("getAll2",response2);
})

However the response is looking something like this: enter image description here

My understanding of when would be that response1 should contain the responseBody of response1 and response2 should contain the responseBody of response2 but that doesnt seem to be the case. What am I missing?


Solution

  • It seems that you are logging jqHXR objects, which are probably a consequence the odd way that jQuery.ajax() and jQuery.when() interact to deliver results see last but one example here.

    In anticipation of purging jQuery at a later stage, I venture to suggest that you need to standardize all your calls at this stage. This is fairly simple, just use .then() instead of .done() and expect results to be delivered in an Array:

    getAll(`/response/${customerId}`, `/response/${methods}`)
    .then(results => {
        console.log("getAll0", results[0]);
        console.log("getAll1", results[1]);
    });
    

    You then need to jump through a few hoops in getAll() in order to standardize various aspects of jQuery.ajax() and jQuery.when() behaviour:

    • standardize jQuery.when()'s delivery results as individual parameters rather than Array.
    • standardize jQuery.ajax()'s delivery of (data, statusText, jqXHR) to its success path.
    • standardize jQuery.ajax()'s delivery of (jqXHR, statusText, errorThrown) to its error path.
    • standardize the behaviour of jQuery.ajax()'s error handler across different versions of jQuery.
    export const getAll = (...ajaxCalls) => {
        function makeAjaxCallAndStandardizeResponse(call) {
            return $.ajax(call)
            .then(
                // success handler: standardize success params
                (data, statusText, jqXHR) => data, // discard statusText and jqXHR
                // error handler: standardize error params and ensure the error does not remain "caught".
                (jqXHR, textStatus, errorThrown) => $.Deferred().reject(new Error(textStatus || errorThrown)).promise(); // discard jqXHR, and deliver Error object on the error path.
            );
        }
        // Aggregate with Promise.all() instead of jQuery.when() to cause a javascript Promise to be returned and results to be delivered as Array.
        return Promise.all(ajaxCalls.map(makeAjaxCallAndStandardizeResponse));
    }
    

    The success handler is probably unnecessary as the Promise.all() aggregation would automatically cause statusText and jqXHR to be discarded, but no real harm in making those discards explicit, unless you need to be obsessive about milliseconds.

    Returning $.Deferred().reject(new Error(textStatus || errorThrown)).promise() from the error handler should give the same behaviour in all versions of jQuery, causing an Error object to be delivered on the error path. (Logged error messages will differ though) Be sure to test this.