What Does $.When.Apply($, Somearray) Do

What does $.when.apply($, someArray) do?

.apply is used to call a function with an array of arguments. It takes each element in the array, and uses each as a parameter to the function. .apply can also change the context (this) inside a function.

So, let's take $.when. It's used to say "when all these promises are resolved... do something". It takes an infinite (variable) number of parameters.

In your case, you have an array of promises; you don't know how many parameters you're passing to $.when. Passing the array itself to $.when wouldn't work, because it expects its parameters to be promises, not an array.

That's where .apply comes in. It takes the array, and calls $.when with each element as a parameter (and makes sure the this is set to jQuery/$), so then it all works :-)

.When() and .done() using an array with .done

If you look at the examples for $.when, you see that the call back gets passed an argument for each promise. If that promise came from an Ajax call, then each argument is an array of the form [ data, statusText, jqXHR ].

So you you just have iterate over the arguments and extract the first element. $.map makes that very easy:

$.when.apply($, requests)
.then(function() {
return $.map(arguments, function(v) {
return v[0];
});
})
.done(callback);

Multiplying two kind of Arrays

Do it like this:

  • Use let for the variable i in the for block, so it is a separate variable that can still be referenced when the loop has completed and you are only then getting the responses from the get calls

  • Use + to convert val() to a number (although in your case it is not absolutely necessary, since the multiplication with the the conversion on-the-fly)

Code:

$("#sendeS1").click(function () {
var Q_Values = [];
var G_Values = [];
var R_Values = [];

for(let i = 1; i <= 20; i++){
$.get("bewertung/get/weight/"+i, function (weight) {
Q_Values[i-1] = +$('#startup_1').find('.'+i).val();
G_Values[i-1] = weight;
R_Values[i-1] = Q_Values[i-1] * G_Values[i-1];
console.log("Q:"+Q_Values);
console.log("G:"+G_Values);
console.log("R:"+R_Values);
});
//...etc

Warning: the arrays will not yet have values when the for loop has completed, so don't assume you have all data right after the loop. See How do I return the response from an asynchronous call? on several ways to deal with that.

One way that is not the nicest code, but the easiest to understand, is to use a count down, so you know when you have all data, and then call a function that will do the further processing:

$("#sendeS1").click(function () {
var Q_Values = [];
var G_Values = [];
var R_Values = [];
var countDown = 20;

for(let i = 1; i <= 20; i++){
$.get("bewertung/get/weight/"+i, function (weight) {
Q_Values[i-1] = +$('#startup_1').find('.'+i).val();
G_Values[i-1] = weight;
R_Values[i-1] = Q_Values[i-1] * G_Values[i-1];
console.log("Q:"+Q_Values);
console.log("G:"+G_Values);
console.log("R:"+R_Values);
countDown--;
if (countDown == 0) processResults(Q_Values, G_Values, R_Values);
});
//...etc

But I would really advise to look into Promises. That really is the way to go. Luckily jQuery $.get returns a promise, and those promises can be passed to $.when, which will call its then method when all these promises have been fulfilled. (See also What does $.when.apply($, someArray) do?).

$("#sendeS1").click(function () {
var Q_Values = [];
var G_Values = [];
var R_Values = [];
var promises = [];

for(let i = 1; i <= 20; i++){
promises.push($.get("bewertung/get/weight/"+i, function (weight) {
Q_Values[i-1] = +$('#startup_1').find('.'+i).val();
G_Values[i-1] = weight;
R_Values[i-1] = Q_Values[i-1] * G_Values[i-1];
console.log("Q:"+Q_Values);
console.log("G:"+G_Values);
console.log("R:"+R_Values);
}));
//...etc
}
$.when.apply($, promises).then(function() {
// all is done, process the results here ...
});

This code could be further improved so that each promise would resolve to its own value, and not yet push the product into an array, but that is something you can maybe look into yourself as an exercise.

Why is $scope.someArray.push not a valid function to pass to .then on $q.promise

You need to bind push since it uses this

returnAPromise('third').then($scope.messages.push.bind($scope.messages));

Any analogue for Q.anySettled promise in JQuery?

Question is indeed similar to one mentioned in comment by Bergi:
"You're right, there is no such method in jQuery. See my answer to the possible duplicate question $.Deferred: How to detect when every promise has been executed for a solution". In the link you can also fine couple of possible workarounds.

Making while for checking the value is correct or not

You could use Promise.all(promises).

  • Your $.post would need to be adapted to create a promise unless it already returns one. (See comment from georg)
  • You would put each promise into an array.
  • Promise.all will be invoked after all async calls complete.
const promises = []
for(let i=0; i<5; i++){
// assuming your post returns a promise
promises.push($.post("somewhere.php"))
}
Promise.all(promises).then(results=>{
// your code here
})

If your code is in an async block, you can do the following and avoid nested functions:

results = await Promise.all(promises)
// your code here
})

This pattern of waiting on all promises is very common after dispatching multiple async calls.

how to resolve all promises in a for loop?

Please try this:

Service.prototype.parse = function(data) {
var detailsArr = [];
for (var i = 0; i < data.length; i++) {
var details = new Details();
detailsArr.push(details);
}

return $.when.apply($, detailsArr).then(function(){
return Array.prototype.slice.call(arguments);
});
};

Here we put all Details into an array, use $.when.apply($, arr) to wait for all Details get resolved. When it's done, each Details' return data will be passed to callback function as one parameter, so the call back function will receive total data.length number of parameters. Then we use Array.prototype.slice.call to convert all parameters to an array and return the result.

For your reference:

What does $.when.apply($, someArray) do?

How can I convert the "arguments" object to an array in JavaScript?

JavaScript Apply

Per the MDN documentation for Function.prototype.apply:

Calls a function with a given this value and arguments provided as an array (or an array like object).

How to make an array of Deferred objects

Thanks to all for great advice.

I used a combination of the suggested techniques in my solution.

The key thing was to make an array of promises, and push onto it the required calls (each with its own array chunk passed as a parameter) to the function that makes the ajax request. One thing I hadn't previously realised is that this calls the ajaxCall() function at that very moment, and that's ok because it returns a promise that is pushed onto the array.

After this, the 'when.apply' line does the trick in waiting until all the ajax promises are fulfilled. The arguments of the 'then' function are used to collate all the results required (obviously, the exact mechanism for that depends on the format of your returned arguments). The results are then sent to theResultsHandler(), which takes the place of the original callback in the code I first posted in my question.

Hope this is useful to other Promise-novices!

The ajax-calling function is:

ajaxCall: function(d) {
return $.ajax('/myURL', {
contentType: 'application/json',
data: d,
dataType: 'json',
type: 'POST'
});
},

And inside the flush() function...

    var promises = [];
var i, j;

for (i=0; i<batchChunks.length; i++)
{
promises.push(self.ajaxCall(batchChunks[i]));
}

var results = [];

return $.when.apply($, promises).then(function(){

console.log("arguments = " + JSON.stringify(arguments));

for (i = 0; i < arguments.length; i++)
{
for (j = 0; j < arguments[i][0].length; j++)
{
results.push(arguments[i][0][j]);
}
}

return self.theResultsHandler(results);
});

How to handle response to several asynchronous request (AJAX calls) made in a loop

The problem is that you need to run in order the methods that are invoked after you get the response to the AJAX calls.

To do so you must undertand that all jQuery AJAX calls return promises. Instead of passing the code to run as a parameter, you can do the following:

var functionToRunAfterResponse(params) = function(params) {
// do something with params
};

var promise = $.post(/*...*/); // some kind of ajax call

promise.then(functionToRunAfterResponse);

Note that the functionToRunAfterResponse will receive the response data as parameter. I.e. it's equivalent to:

promise.then(function(reponseData) {
functionToRunAfterResponse(responseData);
});

This is how it works for a simple call. See $.then and deferred and promise docs.

If you have to make a bunch of calls, you have to do the following:

  • store the promises of the AJAX calls, for example in an array
  • check that all the promises are fulfilled (i.e. that all responses have arrived)
  • run the code in the required order. I.e. run the promise.then in the right order to get the desired result.

To do so, you can use $.when.

The code structure should be like this (pseudocode):

var promises = [];
// Make the calls, and store the promises
for(/**/) {
promises.push( $.post or some other kind of ajax call );
}
// Ensure that all the responses have arrived
$.when.apply($, promises) // See below note
.then(function() {
for each promise in promises
promise[i].then(function(reponseData) {
// run the necessary code
});

NOTE: Here you've got a deeper explanation, but, basically, as $.when expects a series of promises, like this: $.when(promise1, promise2, ...) and you have an array promises[], you have to use apply to make the call, so that the items in the array are passed as individual parameters.

FINAL NOTES:

1) take into account that AJAX calls can fail. In that case, instead of a resolved promise, you get a failed promise. You should check it. If any of the promises fails, $.when will not work as desired:

The method [when] will resolve its master Deferred as soon as all the Deferreds resolve, or reject the master Deferred as soon as one of the Deferreds is rejected.

2) To handle errors, then has two parameters:

$.when.apply().then(function() { /* success */ }, function() { /* error */ });

3) When you make the calls as I've explained, they are all executed in parallell, and the responses will arrive in any order. I.e. there is no warranty that you'll receive all the responses in the order you made the calls. They can even fail, as I explained in 1) That's why you must use when and run then in order again

4) Working with asynchronous methods is great, but you have the responsibility to check that they didn't fail, and run in the right order the code that you need to run after you get the responses.



Related Topics



Leave a reply



Submit