Node.js - Continue promise chain for each result after promise.all() - node.js

I am using Promise.all() within a promise chain. Each promise within the Promise.all() returns a string.
The issue im having is that Promise.all() returns a Promise object to the next promise and I would like to continue the promise chain for each string.
Heres an example:
....
return Promise.all(plugins);
})
.then(function(response) {
console.log(response)
....
The response looks like:
[ 'results from p1', 'results from p2' ]
Is there any way to continue the promise chain for each of the results rather than continuing with a single object containing all results?

Promise.all expects an array of promises. So plugins is an array of promises and all the more: plugin is a Promise.
So you can just chain your plugin Promise.
This would thus become
Promise.all(plugins.map(function(plugin){
return plugin.then(function(yourPluginString){
return 'example '+ yourPluginString;
})
}))

Promise.all(), by its design returns a single promise who's resolved value is an array of resolved values for all the promises you passed it. That's what it does. If that isn't what you want, then perhaps you are using the wrong tool. You can process the individual results in a number of ways:
First, you can just loop over the array of returned results and do whatever you want to with them for further processing.
Promise.all(plugins).then(function(results) {
return results.map(function(item) {
// can return either a value or another promise here
return ....
});
}).then(function(processedResults) {
// process final results array here
})
Second, you could attach a .then() handler to each individual promise BEFORE you pass it to Promise.all().
// return new array of promises that has done further processing
// before passing to Promise.all()
var array = plugins.map(function(p) {
return p.then(function(result) {
// do further processing on the individual result here
// return something (could even be another promise)
return xxx;
});
})
Promise.all(array).then(function(results) {
// process final results array here
});
Or, third if you don't really care when all the results are done and you just want to process each one individually, then don't use Promise.all() at all. Just attach a .then() handler to each individual promise and process each result as it happens.

You can use a tool like https://github.com/Raising/PromiseChain
and implement what you say as
//sc = internalScope
var sc = {};
new PromiseChain(sc)
.continueAll([plugin1,plugin2,plugin3],function(sc,plugin){
return plugin(); // I asume this return a promise
},"pluginsResults")
.continueAll(sc.pluginsResults,function(sc,pluginResult){
return handlePluginResults(pluginResult);
},"operationsResults")
.end();
I didnt test the code if you have any problem PM me

Related

Wait for the nested loops to finish in nodejs

I am very new to node.js and here is my problem
I have a nested set of maps here(I have tried using for-each as well).
All the "get..." methods nested are returning a promise defined by me. I have to loop over the data returned by the methods continuously and use it to call the next "get.." method(there is a dependency). After completing the entire nested loops, the "allobj" array should have my final result and I am trying to return it once all the looping is done. I have not had any luck so far, as an empty array gets returned.
var allobj = new Array();
var catcount = 0;
//categories.forEach(function(category){
var p = categories.map(function(category,index,array){
catcount++;
return gettests(runid,category).then(function(tests){
//console.log(tests.sort().reverse()); //this is sync
//tests.forEach(function(test){
var t = tests.map(function(test,index,array){
return getmetriccategories(runid,category,test).then(function(data){
//console.log(data);
//data.forEach(function(mc){
var d = data.map(function(mc,index,array){
return getmetrics(runid,category,test,mc).then(function(data1){
var x = data1.map(function(metric,index,array){
allobj.push(metric);
return metric;
});
});
});
})
})
})
})
//return when all nested loops are done
res.send(allobj);
Your functions getmetriccategories and getmetrics return Promises, at least, their return values seem to have a then method which is an indicator for that. This means, they work asynchronously. So, the map calls don't return the results directly, but an array of Promises. To wait for all of these Promises to be fulfilled (which means, the asynchronous functions are completed), you can use Promise.all function:
Promise.all(data.map(...))
.then(function (result) {
res.send(result)
})
As you see, Promise.all returns a new Promise, so you can use then to receive the result and send it with res.send.

Using Promise with CouchDB nano & forEach

I need help with debugging this code/or learn efficient way to do it- I tried using bluebird.each to capture all executions within my forEach, but didn't get it work. Same with setting up a new promise with pure javascript. I need help how to execute my forEach FIRST and move on.
let arr = [];
let fields = ['environment', 'habitat', 'earth]
Promise.each(fields, field => {
nano.db.use(field).view('options', 'options')
.spread((body, header) => {
arr.push(body.rows);
})
}).then(() => console.log(arr))
Expected outcome:
arr to console.log ====> [1,2,3,4,5]
Actual outcome:
arr is an empty array ====> [ ]
I see it's a problem with asynchronicity, but I can't seem to figure out how to actually make this work. any input or resources will be greatly appreciated!
I haven't actually ran your code so sorry if I'm incorrect, but from looking at it and the bluebird docs I assume the correction you need to make is return your nano.db call wrapped in a promise inside the Promise.each
let arr = [];
let fields = ['environment', 'habitat', 'earth']
Promise.each(fields, field => {
return new Promise ((resolve, reject) => {
nano.db.use(field).view('options', 'options')
.spread((body, header) => {
arr.push(body.rows);
resolve();
})
});
}).then(() => console.log(arr))
I believe your assumption is right that you're having a problem with asynchronicity when getting an empty array back instead of what you expect. I'm assuming the .then method is firing before the nano.db gets back with the data.
I wrapped your call of nano.db in a promise so that it will await nano.db finishing since Promise.each supports returning promises inside it.
Bluebird's promise docs state with Promise.each.
If the iterator function returns a promise or a thenable, then the
result of the promise is awaited before continuing with next
iteration.
So, if a promise is not returned in your Promise.each and anything that is asynchronous happens inside then just as with a then or a catch method on a promise under the same circumstances.
As I do not know bluebird there may be a way to change that promise to be more bluebird like. The promise I wrapped around the nano.db call is just a normal es6 promise which bluebird may or may not have a different api for creating promises.

For loop in redis with nodejs asynchronous requests

I've got a problem with redis and nodejs. I have to loop through a list of phone numbers, and check if this number is present in my redis database. Here is my code :
function getContactList(contacts, callback) {
var contactList = {};
for(var i = 0; i < contacts.length; i++) {
var phoneNumber = contacts[i];
if(utils.isValidNumber(phoneNumber)) {
db.client().get(phoneNumber).then(function(reply) {
console.log("before");
contactList[phoneNumber] = reply;
});
}
}
console.log("after");
callback(contactList);
};
The "after" console log appears before the "before" console log, and the callback always return an empty contactList. This is because requests to redis are asynchronous if I understood well. But the thing is I don't know how to make it works.
How can I do ?
You have two main issues.
Your phoneNumber variable will not be what you want it to be. That can be fixed by changing to a .forEach() or .map() iteration of your array because that will create a local function scope for the current variable.
You have create a way to know when all the async operations are done. There are lots of duplicate questions/answers that show how to do that. You probably want to use Promise.all().
I'd suggest this solution that leverages the promises you already have:
function getContactList(contacts) {
var contactList = {};
return Promise.all(contacts.filter(utils.isValidNumber).map(function(phoneNumber) {
return db.client().get(phoneNumber).then(function(reply) {
// build custom object
constactList[phoneNumber] = reply;
});
})).then(function() {
// make contactList be the resolve value
return contactList;
});
}
getContactList.then(function(contactList) {
// use the contactList here
}, funtion(err) {
// process errors here
});
Here's how this works:
Call contacts.filter(utils.isValidNumber) to filter the array to only valid numbers.
Call .map() to iterate through that filtered array
return db.client().get(phoneNumber) from the .map() callback to create an array of promises.
After getting the data for the phone number, add that data to your custom contactList object (this is essentially a side effect of the .map() loop.
Use Promise.all() on the returned array of promises to know when they are all done.
Make the contactList object we built up be the resolve value of the returned promise.
Then, to call it just use the returned promise with .then() to get the final result. No need to add a callback argument when you already have a promise that you can just return.
The simplest solution may be to use MGET with a list of phone numbers and put the callback in the 'then' section.
You could also put the promises in an array and use Promise.all().
At some point you might want your function to return a promise rather than with callback, just to stay consistent.
Consider refactoring your NodeJS code to use Promises.
Bluebird is an excellent choice: http://bluebirdjs.com/docs/working-with-callbacks.html
you put async code into a for loop (sync operations). So, each iteration of the for loop is not waiting for the db.client(...) function to end.
Take a look at this stackoverflow answer, it explains how to make async loops :
Here

Callback on last promise from an array of Mongo updates

I would like to have a function called on the return of the last promise made during a forEach() loop.
Sample code:
var mainList = getArrayData()
mainList.forEach((item,i)=>{
mongooseSchema.find(_id:item.id).exec((err,doc)=>{
doStuff(doc)
})
})
Any code after the second block will run immediately after all the mongo queries are sent off. If you wrap the whole thing in a promise (i.e. the array data is from a separate mongo query) you still get the same effect.
Is there any way I can have a special function/callback for the last query, or even better, a promise that returns after all the queries have returned?
Thanks
You could use Promise.all. It will fire off once all of the promises in an array are resolved.
The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first passed promise that rejects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
var mainList = getArrayData()
let promises = [];
mainList.forEach((item,i)=>{
let p = new Promise((resolve, reject) => {
mongooseSchema.find(_id:item.id).exec((err,doc)=>{
doStuff(doc)
resolve();
});
});
promises.push(p);
});
Promise.all(promises).then(() => {
// do something when all are resolved
});

Executing function when chain of promises resolves

I have written code using Q.reduce mechanism where function insertItemIntoDatabase(item) returns resolved promises.
items.reduce(function(soFar,item)
{
return soFar.then(function()
{
return insertItemIntoDatabase(item);
});
},Q());
Is there any possibilty to wait till chain is finished and then execute another function or I should chain those functions in some other way.
Thanks for help in advance
.reduce() will return the final value from the .reduce() loop, which in your case is a final promise. To execute something after the .reduce() chain and all its async operations are done, you just put a .then() handler on that final promise that is returned:
items.reduce(function(soFar,item) {
return soFar.then(function() {
return insertItemIntoDatabase(item);
});
},Q()).then(someOtherFunction);
The purpose of this pattern is exactly that: to return a promise for the result of the sequence. Written out (without reduce), it's exactly equivalent to the expression
Q()
.then(function() { return insertItemIntoDatabase(items[0]); })
.then(function() { return insertItemIntoDatabase(items[1]); })
.then(function() { return insertItemIntoDatabase(items[2]); })
…
You can simple add another .then() on it that will call your callbacks at the end of the chain.

Resources