I'm in the process of replacing some old code that used jQuery Deferred objects and I am rewriting using Bluebird/ES6 Promises.
If I have multiple asynchronous calls, how can I trigger a function after all the promises are resolved.
Using jQuery Deferreds it would be something like this:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
resuests.forEach(function(endpoint) {
promises.push($.ajax({url: endpoint}));
});
$.when.apply($, promises).then(function() {
alert('all promises complete!');
});
How do I rewrite this using ES6 Promise syntax?
Using Promise.all. Note that it takes an iterable such as an Array as its argument, unlike $.when, so doesn't need the .apply.
You'll also want to convert the jQuery Deferred to a native ES6 promise using Promise.resolve(thejQueryDeferred). EDIT: This gets done implicitly by the call to Promise.all, so is really optional.
Whole code:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
requests.forEach(function(endpoint) {
var nativePromise = Promise.resolve($.ajax({url: endpoint})); // if you want to make it clear that you're converting from jQuery Deferred to ES6 promise!
promises.push(nativePromise);
});
Promise.all(promises).then(function() {
alert('all promises complete!');
});
Since this is tagged bluebird in addition to the two good solutions you have already gotten here is a more "bluebird" way:
var requests = [...];
Promise.map(requests, $.get).then(function(results){
alert('all promises complete!');
});
This is probably as simple as it gets.
As the others have pointed out, the native es6 way would be to use Promise.all, no Promise.resolve or explicit creation is needed. The cleanest way with native promises would probably be:
var requests = [...];
Promise.all(requests.map($.get)).then(function(results){
});
Related
I have 2 variable and i want to do something when they changed to same value like each other.
So this is my code:
var val_1,val_2;
for(i=0;i<=5;i++){
readyforgo++;
$.post("somewhere.php",{
something: something
},function(data, status){
readytogo++;
});
}
if(readyforgo==readytogo){
alert(1);
}
Its not gonna return 1 because my if is working faster than my $.post i mean i want some code like this :
function checker(){
if(readyforgo==readytogo){
alert(1);
}else{
setTimeout(function(){checker();},100);
}
}
checker();
Its working as well but i looking for something better i mean i don't want to use any code like that because its using cpu and ram until trying to get readyforgo==readytogo do we have code like this?
i don't like to use setTimeout or setInvertal or something like that.
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.
If you want to check when all the posts have either been resolved/rejected you could take advantage of how jQuery's deferred objects (similar to promises) work. Create an array of jQuery deferred objects and then use when to wait until they've all resolved/rejected.
// Create an array of POST deferred objects
function getDeferreds() {
const deferreds = [];
for (let i = 0; i <= 5; i++) {
deferreds.push($.post("somewhere.php", { something: something }));
}
return deferreds;
}
// $.when works similarly to Promise.all
$.when.apply(null, getDeferreds()).then(data => {
alert(1);
});
Here's a good explanation why apply has been used in this example.
this post extends a previous one already solved. Please see it, to get in context: Nesting promises with $resources in AngularJS 1.0.7
With that approach in mind, I would like to make a parallel call to functions and when both are completed then run the searchBoats function with the results. I think I can nest the promises, but I also think it can be done in parallel, maybe with $q.all. Unfortunately, this promises topic is confusing for me and I don´t know how to do it.
Taking the previous post example, I would like to do something like:
var parseURL = function() {
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(function success(result) {
console.log(result);
searchBoats(result);
});
// Instead of resolve when parseBoatType promise is returned, I would like to
// wait for two promises
parseBoatType().then(deferred.resolve);
parseDestination().then(deferred.resolve);
// of course, with this code deferred will be resolved when first promise
// comes. Is it possible to use $q.all here?
};
parseURL();
As in your earlier question, there is no need to use a deferred here. Just use $q.all(), which returns a promise:
var parseURL = function() {
$q.all([parseBoatType(), parseDestination()])
.then(function (results) {
console.log(results);
searchBoats(results);
});
};
parseURL();
I'm making either 1 or more REST/ajax calls to validate some user information. The rest calls are working well and the information is coming back. The issue I'm facing isn't with that part of the code, which looks something like this.
function ensureUsers(recipients){
var promises = [];
for(var key in recipients){
var payload = {'property':recipients[key]};
promises.push( $.ajax({...}));
}
return $.when.apply($,promises);
}
....
ensureUsers(users) // users is an array of 1 or more users
.done(function(){
console.log(arguments);
)}
If there is more than one user in the initial array, then the arguments in my .done code are structured like this:
[[Object,"success",Object],[Object,"success",Object]...]
I can then iterate over each result, check the status, and proceed.
However if there is only one user in the initial array then .done gets arguments like this:
[Object,"success",Object]
It seems strange to me that the structure of what is returned would change like that. I couldn't find anything about this specific a problem, so I hacked together a solution
var promises = Array.prototype.slice.call(arguments);
if(!Array.isArray(promises[0])){
promises = [promises];
}
Is that really the best I can hope for? Or is there some better way to deal with the returned promises from 1 or more ajax calls in jQuery?
It seems strange to me that the structure of what is returned would change like that.
Yes, jQuery is horribly inconsistent here. When you pass a single argument to $.when, it tries to cast it to a promise, when you pass multiple ones it suddenly tries to wait for all of them and combine their results. Now throw in that jQuery promises can resolve with multiple values (arguments), and add a special case for that.
So there are two solutions I could recommend:
Drop $.when completely and just use Promise.all instead of it:
var promises = [];
for (var p of recipients) {
…
promises.push( $.ajax({…}));
}
Promise.all(promises)
.then(function(results) {
console.log(results);
})
Make each promise resolve with only a single value (unlike $.ajax() that resolves with 3) so that they don't get wrapped in an array, and $.when will produce consistent results regardless of number of arguments:
var promises = [];
for (var p of recipients) {
…
promises.push( $.ajax({…}).then(function(data, textStatus, jqXHR) {
return data;
}) );
}
$.when.apply($, promises)
.then(function() {
console.log(arguments);
})
It appears this functionality is working as currently documented. When you have multiple deferreds passed to $.when it creates a new Promise object that is resolved with the results of each of the results of the passed in deferreds. When you only pass in one, it returns the deferred that you passed in, thus only returning the result instead of an array of results.
I'm not sure if it is any better than your current solution, but you could force it to always have multiple deferreds by having a "fake" deferred that you skip when evaluating the results.
I.E.
function ensureUsers(recipients){
var promises = [$.Deferred().resolve()];
for(var key in recipients){
var payload = {'property':recipients[key]};
promises.push( $.ajax({...}));
}
return $.when.apply($,promises);
}
You could also potentially make it so the placeholder deferred is resolved with the same structure as what you expect in your real results so it would just appear that the first response is always a success.
On node while using thinky.js, I am trying to iterate through a loop and add each item to an array. This however, for some reason is not working.
In another place, it is indentical and working, just without a Promise.then function. Why is this not working?
var fixedItems = [];
for (i in tradeItems) {
var item = tradeItems[i];
Item.get(item["id"]).run().then(function(result) {
var f = { "assetid": result["asset_id"] };
console.log(f); // WOrks
fixedItems.push(f); // Doesn't work
});
}
console.log(fixedItems); // Nothing
A Promise represents the future result of a task. In this case you're logging fixedItems before your tasks (the calls to Item.get) have finished working. In other words, the then functions haven't run yet so nothing has been put into fixedItems.
If you want use fixedItems once it contains all of the items, you'll need to wait for all of the promises to resolve.
How you do that depends on the Promise library you're using. This example, with Promise.all, works with many libraries including native ES6 Promises:
// Get an array of Promises, one per item to fetch.
// The Item.get calls start running in parallel immediately.
var promises = Object.keys(tradeItems).map(function(key) {
var tradeItem = tradeItems[key];
return Item.get(tradeItem.id);
});
// Wait for all of the promises to resolve. When they do,
// work on all of the resolved values together.
Promise.all(promises)
.then(function(results) {
// At this point all of your promises have resolved.
// results is an array of all of the resolved values.
// Create your fixed items and return to make them available
// to future Promises in this chain
return results.map(function(result) {
return { assetid: result.asset_id }
});
})
.then(function(fixedItems) {
// In this example, all we want to do is log them
console.log(fixedItems);
});
Recommended reading: the HTML5 rocks intro to Promises.
Your problem is that you are calling console.log(fixedItems) before any of the promises in the loop have finished executing. A better way of doing this that would also solve the asynchronous problem is to put all the item IDs in an array first and retrieve all the items in a single query, which is also more efficient on the database side.
var itemIds = tradeItems.map(function(item) {
return item.id;
});
var fixedItems = [];
//you would need to write your own getItemsById() function or put the code
//to get the items here
getItemsById(itemIds).then(function(items) {
items.forEach(function(item) {
var f = { "assetid": result["asset_id"] };
fixedItems.push(f);
});
whenDone();
});
function whenDone() {
//you should be able to access fixedItems here
}
I couldn't easily find how to look up multiple records by ID in a single query with thinky, but I did find this page which might help:
http://c2journal.com/2013/01/17/rethinkdb-filtering-for-multiple-ids/
While this would be my preferred way of solving this problem, it would also be possible to still use multiple queries and use a promise chain to wait for them all to be resolved before continuing to your subsequent code. If you wanted to go that route, check out this link: http://promise-nuggets.github.io/articles/11-doing-things-in-parallel.html. (Note: I haven't personally used Bluebird, but I think the Bluebird example in that link may be out of date. The map method appears to be the current recommended way to do this with promises: https://stackoverflow.com/a/28167340/560114.)
Update: Or for this latter option, you can just use the code in joews's answer above.
I'm in the process of replacing some old code that used jQuery Deferred objects and I am rewriting using Bluebird/ES6 Promises.
If I have multiple asynchronous calls, how can I trigger a function after all the promises are resolved.
Using jQuery Deferreds it would be something like this:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
resuests.forEach(function(endpoint) {
promises.push($.ajax({url: endpoint}));
});
$.when.apply($, promises).then(function() {
alert('all promises complete!');
});
How do I rewrite this using ES6 Promise syntax?
Using Promise.all. Note that it takes an iterable such as an Array as its argument, unlike $.when, so doesn't need the .apply.
You'll also want to convert the jQuery Deferred to a native ES6 promise using Promise.resolve(thejQueryDeferred). EDIT: This gets done implicitly by the call to Promise.all, so is really optional.
Whole code:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
requests.forEach(function(endpoint) {
var nativePromise = Promise.resolve($.ajax({url: endpoint})); // if you want to make it clear that you're converting from jQuery Deferred to ES6 promise!
promises.push(nativePromise);
});
Promise.all(promises).then(function() {
alert('all promises complete!');
});
Since this is tagged bluebird in addition to the two good solutions you have already gotten here is a more "bluebird" way:
var requests = [...];
Promise.map(requests, $.get).then(function(results){
alert('all promises complete!');
});
This is probably as simple as it gets.
As the others have pointed out, the native es6 way would be to use Promise.all, no Promise.resolve or explicit creation is needed. The cleanest way with native promises would probably be:
var requests = [...];
Promise.all(requests.map($.get)).then(function(results){
});