vaso123
vaso123

Reputation: 12391

Handling dynamic arguments from $.when in jQuery

I have ajax requests in my requests array. The number of requests could be 1,2 or 3 (does not matter, dynamically).

The requests like this:

var ajaxRequest = $.get(ajaxUrl, requestData);

I am want to process the results like this:

$.when.apply($, requests).then(function () {
    $.each(arguments, function (key, value) {
        console.log(value);
        //var response = JSON.parse(value[0]); //It is fail if there is only 1 ajax request
    });
});

The problem is the arguments.

If there is only 1 request, than at the each, the values will be

string [the json string, what I want to JSON.parse]
string ['success']
Object [the Ajax request Object]

In this case the arguments length is 3, this will cause problems later.

But, if I have 2 or 3 requests, then I will get back arrays like this, (for example for 2 requests):

Array [json string, 'success', ajax Object],
Array [json string, 'success', ajax Object]

If three requests, of course one more array.

And the problem is here. I can not check the length of arguments, because it is 3 if I have 3 requests, and 3 if I have only one requests.

So I rewrote my script like this:

$.each(arguments, function (key, value) {
    if (typeof value === 'string') {
        var response = JSON.parse(value);
    } else {
        var response = JSON.parse(value[0]);
    }
    $('div[data-id="' + response.productId + '"').find('.dictionaryContainer').find('ul').html(response.html);
});

Now my problem is here:

if (typeof value === 'string') {

even if it is good, the iteration is continuous, maybe it could be hacked somehow with flags, and return tures, but I bet, there is some more elegant solution for this.

Can anybody help me to handle this?

Upvotes: 1

Views: 535

Answers (2)

T J
T J

Reputation: 43156

Simple work around would be to check the length of requests yourself and adjust what you're iterating on, something like this:

$.when.apply(null, requests).done(function() {
 var arg = (requests.length === 1) ? [arguments] : arguments;
 $.each(arg, function(i, response) {
   console.log(response);
 })
});

Something like this fiddle

Upvotes: 2

T.J. Crowder
T.J. Crowder

Reputation: 1074148

Unfortunately, as you've found, the combination of $.when and the kind of promise is essentially broken because of its chaotic behavior that depends on whether you pass one or two+ arguments to $.when.

I'd suggest either:

  1. Using promises (with a polyfill if needed) and promise-ifying $.ajax, $.get, etc.; then use Promise.all on the array of promises.

  2. Implementing your own $.whenAll that isn't chaotic.

Here are rough version of both (just for $.ajax, you can do your own $.get and $.post readily enough):

Rough version of #1: (Note: Creating a new, clean promise rather than reusing $.ajax's promise in order to just return the result [not success's other arguments] and return a single object with a rejection [not error's various discrete arguments].)

$.promiseAjax = function(options) {
    return new Promise(function(resolve, reject) {
        options = $.extend({}, {
            success: function(result) {
                resolve(result);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                reject({
                    jqXHR: jqXHR,
                    textStatus: textStatus,
                    errorThrown: errorThrown
                });
            }
        }, options);
        $.ajax(options);
    });
};

Usage:

var requests = [];
if (someCondition) {
    requests.push($.promiseAjax({/*...*/}));
}
if (someotherCondition) {
    requests.push($.promiseAjax({/*...*/}));
}
Promise.all(requests).then(function(results) {
    // `results` will reliably be an array of the results, even when
    // there's only one request
});

Rough version of #2:

$.whenAll = function(promises) {
    var d = $.Deferred();
    var results = [];
    var counter = promises.length;
    promises.forEach(function(promise, index) {
        promise
            .done(function(result) {
            results[index] = result;
            if (--counter == 0) {
              // All done
                d.resolve(results);
            }
          })
          .fail(function() {
            d.reject(Array.prototype.slice.call(arguments));
          });
    });
    return d.promise();
};

usge:

var requests = [];
if (someCondition) {
    requests.push($.ajax({/*...*/}));
}
if (someotherCondition) {
    requests.push($.ajax({/*...*/}));
}
$.whenAll(requests).then(function(results) {
    // `results` will reliably be an array of the results, even when
    // there's only one request
});

Upvotes: 1

Related Questions