How to Use Promise in Foreach Loop of Array to Populate an Object

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.

  1. Promises in Parallel: Promise.all all the promises object and use array#map() to create a array of ticket objects.

  2. Promises in Series: await each promise and construct the ticket one by one.

If you want to do a Promises in Parallel way, you can do like below:

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?

  1. First, move the code flow that deals with each individual user value into its own separate async function.

    • Because of how async functions actually work (as each async function marks the boundary of each async state-machine) it isn't really feasible to do concurrent-async work inside a for statement.
  2. Invoke that function from within the for loop.

  3. Always prefer === over ==.

  4. Use TypeScript. Using async JavaScript without TypeScript is a world of pain because you need to keep track of which functions return Promise<T> vs. those that don't, as well as correctly unwrapping Promise<T> objects.

  5. Always, wherever possible, have your own try/catch statement that wraps the entire body of an async 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 awaiting each of the Promises 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 thens, 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.

And some tips:

  • Mapping is better done with .map than with for/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.

Ok, so let's get started:

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



Leave a reply



Submit