How to use promise in forEach loop of array to populate an object
Promise.all()
will be helpful here:
var promises = [];
array.forEach(function(element) {
promises.push(
developer.getResources(element)
.then((data) = > {
name = data.items[0];
return developer.getResourceContent(element, file);
})
.then((response) = > {
fileContent = atob(response.content);
self.files.push({
fileName: fileName,
fileType: fileType,
content: fileContent
});
}).catch ((error) = > {
console.log('Error: ', error);
})
);
});
Promise.all(promises).then(() =>
self.resultingFunction(self.files)
);
This starts the AJAX call for each of the items, adds the result of each call to self.files
once the call is complete and calls self.resultingFunction()
after all calls have been completed.Edit: Simplified based on Yury Tarabanko's suggestions.
Populate an array in the loop using promise
Promise.all(res.data.map(async (post) => {
if (post.categories.includes(NEWS_CATEGORY_ID)) {
const response = await getMediaInfo(post.featured_media);
post = {...post, featured_url: response};
return post;
}
})).then(postsWithImageURLS => console.log(postsWithImageURLS));
You should access postsWithImageURLS after all async methods finish. How can you resolve promises in multiple nested forEach loops?
The main issue your codes doesn't work because you try to Promise.all() to a array of the ticket
below.
Promise.all only working on promises object, but you can see the ticket
itself is not a purely Promise object. Also, the map() function will not await the promise in the loop. If you want to await a promise in a loop, you need to use for loop
.
let confirmation_number = await generateConfirmationNumber();
let ticket = {
confirmation_number: confirmation_number,
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "1ST CL",
flight_class: "first",
seat: {
isle: seat.letter,
number: i + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: generatePassenger(),
};
The below codes doesn't work because ticket is not a promise object.Promise.all([ticket1,ticket2,ticket3]);
To make it work, you have two options.Promises in Parallel: Promise.all all the promises object and use
array#map()
to create a array of ticket objects.Promises in Series: await each promise and construct the ticket one by one.
const arrayOfPromises = flight.plane.sections.first_class.rows.flatMap(row =>
row.map(seat => generateConfirmationNumber())
);
const confirmationNumbers = await Promise.all(arrayOfPromises);
const tickets = confirmationNumbers.map(cfmNumber => ({
confirmation_number: cfmNumber,
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "1ST CL",
flight_class: "first",
seat: {
isle: seat.letter,
number: i + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: generatePassenger(),
}));
The answer I use is 2nd option(Promises in Series).I use for of loop to await each promise and create a ticket from it.
In the end, I add ticket to a array tickets.
async function generateTickets(flight) {
let tickets = [];
let prices = generatePrice();
const firstClass = [];
for (const row of flight.plane.sections.first_class.rows) {
for (const seat of row) {
if (!seat.isle) {
const passenger = generatePassenger();
const confirmation_number = await generateConfirmationNumber();
const ticket = {
confirmation_number: "",
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "1ST CL",
flight_class: "first",
seat: {
isle: seat.letter,
number: flight.plane.sections.first_class.rows.indexOf(row) + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: passenger,
};
if (passenger.first_name !== "" || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number;
}
firstClass.push(ticket);
}
}
}
console.log(firstClass);
const preferredClassTickets = [];
for (const row of flight.plane.sections.preferred_class.rows) {
for (const seat of row) {
if (!seat.isle) {
let passenger = generatePassenger();
let confirmation_number = await generateConfirmationNumber();
let ticket = {
confirmation_number: "",
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "PREF PLUS",
flight_class: "preferred",
seat: {
isle: seat.letter,
number: flight.plane.sections.preferred_class.rows.indexOf(row) + flight.plane.sections.first_class.total_rows + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.preferred,
passenger: passenger,
};
if (passenger.first_name !== "" || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number;
}
preferredClassTickets.push(ticket);
}
}
}
const economyClass = [];
for (const row of flight.plane.sections.economy_class.rows) {
for (const seat of row) {
if (!seat.isle) {
let passenger = generatePassenger();
let confirmation_number = await generateConfirmationNumber();
let ticket = {
confirmation_number: "",
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "PREF PLUS",
flight_class: "preferred",
seat: {
isle: seat.letter,
number:
flight.plane.sections.economy_class.rows.indexOf(row) +
flight.plane.sections.first_class.total_rows +
flight.plane.sections.preferred_class.total_rows +
1,
},
boarding_zone: generateBoardingZone(),
price: prices.economy,
passenger: passenger,
};
if (passenger.first_name !== "" || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number;
}
economyClass.push(ticket);
}
}
}
tickets.push(...firstClass, ...preferredClassTickets, ...economyClass);
return tickets;
}
async function generateConfirmationNumber() {
let letters = "ABCDEFGHJKLMNPRSTUVWXYZ";
let number = Math.floor(Math.random() * 10000);
let confirmation =
letters[Math.floor(Math.random() * letters.length)] +
letters[Math.floor(Math.random() * letters.length)] +
letters[Math.floor(Math.random() * letters.length)] +
number;
return new Promise((resolve, reject) => {
Flight.findOne(
{ "tickets.confirmation_number": confirmation },
(err, result) => {
if (err) reject(error);
// else if (result) return resolve(generateConfirmationNumber());
else if (result) return resolve(result);
else return resolve(confirmation);
}
);
});
}
const returnFlight = flight => {
return new Promise((resolve, reject) => {
Flight.find(
{ $or: [{ number: flight.number }] },
function (error, records) {
if (error) reject(error);
if (records.length) {
console.log("Similar flight already exists");
resolve(records);
} else {
flight.save(function (error, result) {
if (error) {
console.log("ERROR:" + error);
reject(error);
} else {
console.log("Flight " + result.number + " successfully saved.");
resolve(result);
}
});
}
}
);
});
};
const create_flights = async () => {
let flights_array = [];
// let flights_with_tickets = [];
for (let i = 0; i < 1; i++) {
let airline = randomAirline();
let origin = randomAirport();
let destination = randomDestination(origin.code);
let duration = generateDuration();
let departure = generateDeparture();
let departure_date = dateString(departure);
let flight_data = {
number: generateFlightNumber(airline.code),
airline: airline.name,
plane: {
name: randomPlane(),
},
origin: origin,
destination: destination,
departure: departure,
departure_date: departure_date,
duration: duration,
gate: generateGate(),
amenities: randomAmenities(duration),
tickets: [],
};
const flight = new Flight(flight_data);
flights_array.push(flight);
}
console.log("FLIGHTS ARRAY");
console.log(flights_array);
for (let flight of flights_array) {
const tickets = await generateTickets(flight);
if (tickets) {
flight.set({
tickets: tickets
})
const record = await returnFlight(flight);
console.log(record);
if (record) {
console.log("Created Flight");
console.log(record.tickets);
}
}
}
};
async function createFlight(req, res) {
try {
await create_flights();
console.log("Done");
res.redirect("/");
} catch (err) {
console.log(err);
}
}
How to execute promises simultaneously with forEach?
First, move the code flow that deals with each individual
user
value into its own separateasync
function.- Because of how
async
functions actually work (as eachasync function
marks the boundary of each async state-machine) it isn't really feasible to do concurrent-async work inside afor
statement.
- Because of how
Invoke that function from within the
for
loop.Always prefer
===
over==
.Use TypeScript. Using
async
JavaScript without TypeScript is a world of pain because you need to keep track of which functions returnPromise<T>
vs. those that don't, as well as correctly unwrappingPromise<T>
objects.Always, wherever possible, have your own
try/catch
statement that wraps the entire body of anasync
function, otherwise you'll have to deal with inconsistencies with how different JS engines and JS code handles thrown exceptions and objects.
import usersjson from './jsons/users.json';
async function updateUsers() {
const promises = [];
const users = usersjson;
for( const user of users ) {
const promise = updateUser( user ); // <-- Note this is *NOT* awaited here.
promises.push( promise );
}
await Promise.all( promises ); // <-- This is where we await everything.
}
async function updateUser( user ) {
try {
const data = await getUser( url, user.Id, 'Id' );
if( data.users.length === 0 ) {
let createResult = await newUser( url, user );
console.log( "Created new user %o", createResult );
}
else {
const updateResult = await updateUser( url, user, data.users[0].id );
console.log( "Update user %o: Result: %o", user.Id, updateResult );
}
}
catch( err ) {
console.error( "Failed to update (or create) user with UserId %o: %o", user.userId, err );
}
}
How to wait a Promise inside a forEach loop
It sounds like you want to wait for each Promise
to resolve before initializing the next: you can do this by await
ing each of the Promise
s inside an async
function (and you'll have to use a standard for
loop to asynchronously iterate with await
):
const example1 = () => new Promise(function(resolve, reject) { setTimeout(function() { resolve('foo1'); }, 500);});
const example2 = () => new Promise(function(resolve, reject) { setTimeout(function() { resolve('foo2'); }, 500);});
const doStuff = async () => { const listExample = ['a','b','c']; for (let i = 0; i < listExample.length; i++) { console.log(listExample[i]); await example1(); const s = listExample[i]; console.log("Fisrt"); await example2(); console.log("Second"); } console.log("The End");};
doStuff();
foreach loop with promise execute API call AFTER the first one is done
async function asyncExample() {
let array = [1,2,3];
for (let i = 0; i < array.length; i++) {
var data = await fetch('https://reqres.in/api/products/'+item);
});
}
You have to use Async/Await to make the code wait for the API call. Async/await doesn't work properlly with loop's, to avoid problens with that you should use a normal for
ou a for of
. More examples: https://zellwk.com/blog/async-await-in-loops/ Node JS Promise.all and forEach
It's pretty straightforward with some simple rules:
- Whenever you create a promise in a
then
, return it - any promise you don't return will not be waited for outside. - Whenever you create multiple promises,
.all
them - that way it waits for all the promises and no error from any of them are silenced. - Whenever you nest
then
s, you can typically return in the middle -then
chains are usually at most 1 level deep. - Whenever you perform IO, it should be with a promise - either it should be in a promise or it should use a promise to signal its completion.
- Mapping is better done with
.map
than withfor/push
- if you're mapping values with a function,map
lets you concisely express the notion of applying actions one by one and aggregating the results. - Concurrency is better than sequential execution if it's free - it's better to execute things concurrently and wait for them
Promise.all
than to execute things one after the other - each waiting before the next.
var items = [1, 2, 3, 4, 5];
var fn = function asyncMultiplyBy2(v){ // sample async action
return new Promise(resolve => setTimeout(() => resolve(v * 2), 100));
};
// map over forEach since it returns
var actions = items.map(fn); // run the function over all items
// we now have a promises array and we want to wait for it
var results = Promise.all(actions); // pass array of promises
results.then(data => // or just .then(console.log)
console.log(data) // [2, 4, 6, 8, 10]
);
// we can nest this of course, as I said, `then` chains:
var res2 = Promise.all([1, 2, 3, 4, 5].map(fn)).then(
data => Promise.all(data.map(fn))
).then(function(data){
// the next `then` is executed after the promise has returned from the previous
// `then` fulfilled, in this case it's an aggregate promise because of
// the `.all`
return Promise.all(data.map(fn));
}).then(function(data){
// just for good measure
return Promise.all(data.map(fn));
});
// now to get the results:
res2.then(function(data){
console.log(data); // [16, 32, 48, 64, 80]
});
Promise inside foreach loop in typescript, construct array from response and wait for all done
I think what you are looking for here is Promise.all
. Assemble the calls to your rest endpoint as an array of tasks, something along the lines of:
const tasks = suites.map(suite => testManagementRestClient.getPoints(...
And then to wait for all those tasks to resolve, use Promise.all
as follows:
await Promise.all(tasks);
You can find more about Promise.all
here
How to loop thorugh mongoDB with a promise and a forEach loop? I want to populate an Array
You need to render it after the Promises all resolve using Promise.prototype.all
, there's no other way to get the populated productsArr
array.
Try this:
//shopController.js
const Product = require('../models/product');
const User = require('../models/user');
exports.getCart = (req, res, next) => {
const productIds = [];
// const productsArr = []; not needed anymore
let userData = null;
User.findById(req.user._id)
.then(user => {
userData = user;
userData.cart.items.map(prodId => {
productIds.push(prodId.productId);
})
console.log(productIds); // [ 5dc1b6ace13a97588620d6c6, 5dc1b6ace13a97588620d6c6 ]
return productIds;
})
.then(prodIds => {
Promise.all(prodIds.map(prodId =>
Product.findById(prodId)
).then(productsArr => {
res.render('shop/cart', {
path: '/cart',
docTitle: 'Cart',
products: productArr,
userData: userData
});
console.log(productsArr); // [ ... products ... ]
}).catch(err => {
console.log(err);
})
})
.catch((err) => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
}
Related Topics
JavaScript Timestamp to Relative Time
Remove Whitespace and Line Breaks Between HTML Elements Using Jquery
React Doesn't Reload Component Data on Route Param Change or Query Change
Localstorage Object Is Undefined in Ie
Cross Domain Localstorage with JavaScript
Sort an Array by the "Levenshtein Distance" with Best Performance in JavaScript
Does Use of Anonymous Functions Affect Performance
JavaScript Regular Expression to Check for Ip Addresses
How Does Trello Access the User's Clipboard
Why Do Empty JavaScript Arrays Evaluate to True in Conditional Structures
What Is Firebase Firestore 'Reference' Data Type Good For
JavaScript Object Literals Syntax Error
Understanding JavaScript Promises; Stacks and Chaining