I'm trying to resolve multiple promises and return something when all of them have resolved
I've looked around but can't find a solution or I'm just not understanding.
My Code:
export const setLeagues = (res: any, leaguesArray: any) => {
leaguesArray.forEach((element: any) => {
firebaseAdmin.firestore().collection('leagues').add(element)
.catch((err: any) => { res.send(err) })
})
}
I want to do a res.send('Successfully Added!) when all promises in the forEach has resolved.
This is simple enough if you use Promise.all and map:
export const setLeagues = (res: any, leaguesArray: any) => {
// Wrap in a Promise.all, and use .map instead of .forEach:
Promise.all(leaguesArray.map((element: any) => {
// Make sure to return promise here...
return firebaseAdmin.firestore().collection('leagues').add(element)
.catch((err: any) => { res.send(err) })
})).then(() => {
// Do things here that need to wait for all promises to resolve.
})
}
I'll note that due to the way Promise.all works, you may want to change the way you handle an error as well. Promise.all rejects as soon as any of it's wrapped promises reject, or resolves when all of its wrapped promises resolve. So I would recommend moving your .catch to chain off the Promise.all rather than the inner promises to avoid sending a response twice:
export const setLeagues = (res: any, leaguesArray: any) => {
// Wrap in a Promise.all, and use .map instead of .forEach:
Promise.all(leaguesArray.map((element: any) => {
// Make sure to return promise here...
return firebaseAdmin.firestore().collection('leagues').add(element)
})).then(() => {
// Do things here that need to wait for all promises to resolve.
res.send('Successfully Added!')
}).catch((err: any) => {
// Consider moving error handler to here instead...
res.send(err)
})
}
You'll have to collect all your promises into and array and use Promise.all() to generate a new promise that's resolved only after each one of them is resolved. The general form is like this:
const promises = []
things.forEach(thing => {
promises.push(createPromiseForWork(thing))
})
const p = Promise.all(promises)
p.then(() => {
// continue with your work
})
Related
.then(async (rows) => {
//some code
response = await sendEmail(email);
}
Hello, is it acceptable to make the then method in promises asynchronous if we refer to another interface to send email?
While this would work, IMO it's bad style to mix async/await with promise chaining. Why not just
fooPromise()
.then(rows => {
...
return sendEmail(email);
})
.then(response => {
...
})
or
async function foo() {
const rows = await fooPromise();
...
const response = await sendEmail(email);
...
}
Ie, chose one way you like better and stick with it.
Agree with the answer from #derpirscher. The async/await syntax is mostly just syntactic sugar for promises but it can do wonders for legibility. Also you can try:
async function doSomething() {
let response;
try {
response = await sendEmail(email);
} catch (err) {
console.log(err);
return []
}
return response
You can find a resource about async javascript and promises here.
I'm trying to write a collection of Json objects into an array whilst looping through a fetch operation in Node JS. I'm sure the issue is something to do with it being an Async operation, but can't figure out how to get round it.
This is my code:
for (const car of carFilter) {
const carJson = fetch(modelUrl + car, settings)
.then(res => res.json())
.then((json.data.trim));
carData.push(carJson);
}
console.log(carData);
All I'm getting from the console.log is:
Promise { <pending> },
Promise { <pending> },... etc
Which I presume means I'm trying to do the console.log before the data has been push into the array. I may be wrong.
Thanks in advance.
You can do something like this:
const promises = [];
for (const car of carFilter) {
const carJson = fetch(modelUrl + car, settings)
promises.push(carJson);
}
Promise.all(promises)
.then((values) => {
console.log(values); // will be in same order they are called
console.log(values[0]); // will be an Array
})
.catch( e => console.log(e));
So, When we call out an async operation, it returns a Promise(in this case). We are pushing all the promises in an array and can use "Promises.all" that can help to wait for all of them to resolve and gives results.
Note: if any of your promise will be rejected, you won't be able to get the subsequent promises resolved or rejected.
Sample one is here:
const promises = [];
for (let i = 0; i < 10; i++) {
const carJson = promiseCall(); //any promise call
promises.push(carJson);
}
Promise.all(promises)
.then((values) => {
console.log(values); // will be in same order they are called
})
.catch( e => console.log(e));
function promiseCall () {
return new Promise((res, rej) => {
setTimeout(()=> {
res(true);
} ,1000);
})
}
I am making a test app using systeminformation. I'm trying to make it so that each then waits for the previous function to finish. The problem I'm having is that the functions I am running inside are also promises, so the next then runs before the function finishes.
const si = require('systeminformation');
var cpuObj;
function initCPU() {
return new Promise(resolve => {
si.cpu()
.then(data => cpuObj = data)
.catch(err => console.log(err))
.then(() => {
setTimeout(() => console.log("timer"), 3000);
})
.then(() => {
si.cpuTemperature().then(data => console.log(data));
})
.then(() => {
console.log("here");
});
});
}
function test() {
console.log(cpuObj);
}
initCPU().then(() => {
test();
});
Output:
here
{ main: -1, cores: [], max: -1 }
timer
Expected Output:
{ main: -1, cores: [], max: -1 }
timer
here
A few points that need to be addressed:
setTimeout() does not return a promise, so you need to promisify and return it.
Flatten your chain by returning the promises from within each of the continuations rather than attempting to chain continuations within other continuations (i.e. then() inside of then()).
Do not wrap the continuation chain with a promise constructor, as the chain itself is already a promise, just return it directly instead. This is considered an antipattern.
Do not use globals, because it makes the initCPU() no longer re-entrant safe. Multiple calls to initCPU() before the promise returned by the first call resolves will result in unexpected behavior otherwise. Instead, use the appropriate scope to pass values along, which in this case is the function itself.
Allow errors to propagate to the caller and let the caller decide how to handle the error. Do not handle errors from within initCPU() unless you expect to use a fallback and continue to provide meaningful data to the caller.
const si = require('systeminformation');
const delay = ms => new Promise(resolve => { setTimeout(resolve, ms); });
function initCPU() {
// use local scope, not global
let cpuObj;
// return this promise chain directly
return si.cpu()
.then(data => {
cpuObj = data;
// return the promise to the chain
return delay(3000);
})
// let caller handle errors
// .catch(err => console.log(err))
// flatten your chain
.then(() => {
console.log('timer');
// return the promise to the chain
return si.cpuTemperature();
})
// flatten your chain
.then(data => {
console.log(data);
console.log('here');
// pass data to caller
return cpuObj;
});
}
function test(cpuObj) {
// received from last continuation of initCPU()
console.log(cpuObj);
}
initCPU()
.then(test)
// handle error from caller
.catch(err => {
console.log(err);
});
If you just want to query the cpu object immediately, and query cpuTemperature after 3 seconds, I'd do something like this using Promise.all():
// default to 3 seconds, allow it to be configurable
function initCPU(ms = 3000) {
return Promise.all([
si.cpu(),
delay(ms).then(() => si.cpuTemperature())
]).then(([cpu, cpuTemperature]) => ({
cpu,
cpuTemperature
}));
}
function test (obj) {
console.log(obj.cpu);
console.log(obj.cpuTemperature);
}
initCPU()
.then(test)
.catch(err => {
console.log(err);
});
I have the following code. I expect the output: START,Middle,Middle,END
but instead I get this START,Middle,END,Middle
(FYI prices array has 2 values in my example)
console.log("START");
await Promise.all(prices.map(async(price) => {
let obj: any = {};
obj.normal = price.normal;
await new Transport(obj).save(async (err: any, doc: any) => {
console.log("Middle");
price.transport_id = doc._id;
});
}));
console.log("END");
console.log(prices);
Change the inner await to a return statement, otherwise prices.map() is generating an array of entries that are undefined instead of promises.
Since Transport#save() does not return a promise, you'll need to wrap it with a Promise constructor since it is a callback-style API, or refer to the documentation which may perhaps explain how to instead return a promise.
To wrap it, you could do something like this:
// ...
obj.normal = price.normal;
return new Promise((resolve, reject) => {
new Transport(obj).save((err: any, doc: any) => {
console.log('Middle');
if (err) return reject(err);
price.transport_id = doc._id;
resolve(price);
});
});
In node.js I am trying to loop through some items, complete an async process for each one and then wait for each to be complete before the next one starts. I must be doing something wrong as the Promise.all() is not waiting for any of the async processes to complete! My code is below:
getChildLessons() {
return new Promise((resolve, reject) => {
Promise.all(
//nested for loop is needed to get all information in object / arrays
this.lessons.levels.map((item, i) => {
item.childlevels.map((childItem, iChild) => {
return ((i, iChild) => {
//return async process with Promise.resolve();
return this.horseman
.open(childItem.url)
.html()
.then((html) => {
//adding some information to an array
})
.then(() => {
return Promise.resolve();
}).catch((err) => {
reject(err);
});
})(i, iChild);
});
})
// Promise.all().then()
).then(() => {
resolve(this.lesson);
})
.catch((err) => {
console.log(err);
});
});
}
I am fairly new to async with node.js so please could you provide a clear example if possible.
Two issues that need to be fixed to make it work:
The callback provided to the outer map call does not have a return statement, so by consequence that map creates an array in which all elements are undefined. You need to return the result of child.items.map, i.e. an array of promises.
Once the above is fixed, the outer map will return an array of arrays. That 2D array of promises needs to be flattened to a simple array of promises. You can do this with [].concat and the spread syntax.
So the first lines of your code should become:
Promise.all(
[].concat(...this.lessons.levels.map((item, i) => {
return item.childlevels.map((childItem, iChild) => {
Add a closing parenthesis -- to close the argument list of concat( -- at the appropriate spot.
Some other remarks:
The following code is useless:
.then(() => {
return Promise.resolve();
})
The promise on which .then is called is by definition resolved at the moment the callback is called. To return a resolved promise at that very moment does not add anything useful. To return the promise on which .then is called is just fine. You can just remove the above .then call from the chain.
Near the end you call resolve(this.lesson). This is an example of the promise constructor anti pattern. You should not create a new Promise, but instead return the result of the Promise.all call, and inside its .then call return this.lesson so that it becomes the promised value.
Chaining all the promises
To chain all the promises instead of using Promise.all, it is easiest if you use the async/await syntax. Something like this:
async getChildLessons() {
for (let item of this.lessons.levels) {
for (let childItem of item.childlevels) {
let html = await this.horseman
.open(childItem.url)
.html();
//adding some information to an array
// ...
}
}
return this.lesson;
}
maybe try doing something like this ?
let firstPromise = new Promise((resolve,reject) => {
// your logic/code
})
//add more promises you want into array if u want
Promise.all([firstPromise])
.then((response) => {
//do stuff with response
})
.catch((error) => {
//do stuff with error
})
The line of code below fails to return anything. You are attempting to pass an array of undefined to Promise.all
this.lessons.levels.map((item, i) => {...})
There are several more problems with your code, though. The block below is completely unnecessary. It literally does nothing except add an extra block to your code
return ((i, iChild) => {...})(i, iChild);
You don't need to return a Promise from the main function. The result of Promise.all() IS a Promise.
Taking the above into account, here is a code snippet.
// an array of Promises
var levelPromises = this.lessons.levels.map((item, i) => {
var childPromises = item.childlevels.map((childItem, iChild) => {
return this.horseman.open(childItem.url)
//...
})
return Promise.all(childPromises)
})
return Promise.all(levelPromises)