1

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?

2
  • Ok, I think, I need to check the arguments variable before the $.each. Now I am working on this. Commented Jan 3, 2017 at 10:42
  • Yes, this will be the solution. Before $.each I check, if arguments[0] is exists, and if yes, and this is a string, then just 1 request has sent, but if it is an object, I need to use $.each. Commented Jan 3, 2017 at 10:45

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

1 Comment

That's a nice, simple, direct approach, looking at requests to know whether to fix-up the arguments. Suggested tweak: var args = Array.prototype.slice.call(arguments); var arg = requests.length === 1 ? [args] : args;` just so things are reliably arrays.
1

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
});

2 Comments

I will check these solutions later, second seems more clear to me. Now I solved with my solution, what is check the arguments[0]. If that is a string, then JSON.parse(arguments[0]), else iterate on each arguments. Yes, I know, it is not safe, and do not handle fails, but for now it's ok me, later I will go deep down into your second solution. Thank you.
@karacsi_maci: If you're looking for a minimal, one-off solution, take a look at T J's updated answer, which uses a nice, simple, direct approach to dealing with the ambiguity. About the answer above: Proper promises are the way forward for things like this, where we want to wait for a bunch of parallel ops to finish (and some other forms of composition). (When just doing a bunch of async things in series they're the current way, but will probably give way to async/await in a couple of years.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.