Hi I need to execute promises one after the other how do I achieve this using promise.all any help would be awesome. Below is the sample of my code I am currently using but it executes parallel so the search will not work properly
public testData: any = (req, res) => {
// This method is called first via API and then promise is triggerd
var body = req.body;
// set up data eg 2 is repeated twice so insert 2, 5 only once into DB
// Assuming we cant control the data and also maybe 3 maybe inside the DB
let arrayOfData = [1,2,3,2,4,5,5];
const promises = arrayOfData.map(this.searchAndInsert.bind(this));
Promise.all(promises)
.then((results) => {
// we only get here if ALL promises fulfill
console.log('Success', results);
res.status(200).json({ "status": 1, "message": "Success data" });
})
.catch((err) => {
// Will catch failure of first failed promise
console.log('Failed:', err);
res.status(200).json({ "status": 0, "message": "Failed data" });
});
}
public searchAndInsert: any = (data) => {
// There are database operations happening here like searching for other
// entries in the JSON and inserting to DB
console.log('Searching and updating', data);
return new Promise((resolve, reject) => {
// This is not an other function its just written her to make code readable
if(dataExistsInDB(data) == true){
resolve(data);
} else {
// This is not an other function its just written her to make code readable
insertIntoDB(data).then() => resolve(data);
}
});
}
I looked up in google and saw the reduce will help I would appreciate any help on how to convert this to reduce or any method you suggest (Concurrency in .map did not work)
Hi I need to execute promises one after the other how do I achieve this using promise.all any help would be awesome. Below is the sample of my code I am currently using but it executes parallel so the search will not work properly
public testData: any = (req, res) => {
// This method is called first via API and then promise is triggerd
var body = req.body;
// set up data eg 2 is repeated twice so insert 2, 5 only once into DB
// Assuming we cant control the data and also maybe 3 maybe inside the DB
let arrayOfData = [1,2,3,2,4,5,5];
const promises = arrayOfData.map(this.searchAndInsert.bind(this));
Promise.all(promises)
.then((results) => {
// we only get here if ALL promises fulfill
console.log('Success', results);
res.status(200).json({ "status": 1, "message": "Success data" });
})
.catch((err) => {
// Will catch failure of first failed promise
console.log('Failed:', err);
res.status(200).json({ "status": 0, "message": "Failed data" });
});
}
public searchAndInsert: any = (data) => {
// There are database operations happening here like searching for other
// entries in the JSON and inserting to DB
console.log('Searching and updating', data);
return new Promise((resolve, reject) => {
// This is not an other function its just written her to make code readable
if(dataExistsInDB(data) == true){
resolve(data);
} else {
// This is not an other function its just written her to make code readable
insertIntoDB(data).then() => resolve(data);
}
});
}
I looked up in google and saw the reduce will help I would appreciate any help on how to convert this to reduce or any method you suggest (Concurrency in .map did not work)
Share Improve this question edited Feb 25, 2019 at 14:04 Akshay Venugopal asked Feb 25, 2019 at 13:28 Akshay VenugopalAkshay Venugopal 4713 gold badges9 silver badges22 bronze badges 11 | Show 6 more comments4 Answers
Reset to default 12the Promises unfortunatelly does not allow any control of their flow. It means -> once you create new Promise, it will be doing its asynchronous parts as it likes.
The Promise.all
does not change it, its only purpose is that it checks all promises that you put into it and it is resolved once all of them are finished (or one of them fail).
To be able to create and control asynchronous flow, the easiest way is to wrap the creation of Promise into function and create some kind of factory method. Then instead of creating all promises upfront, you just create only one promise when you need it, wait until it is resolved and after it continue in same behaviour.
async function doAllSequentually(fnPromiseArr) {
for (let i=0; i < fnPromiseArr.length; i++) {
const val = await fnPromiseArr[i]();
console.log(val);
}
}
function createFnPromise(val) {
return () => new Promise(resolve => setTimeout(() => resolve(val), 200));
}
const arr = [];
for (let j=0; j < 10; j++) {
arr.push(createFnPromise(j));
}
doAllSequentually(arr).then(() => console.log('finished'));
PS: It is also possible without async/await using standard promise-chains, but it requires to be implemented with recursion.
If anyone else cares about ESLint complaining about the use of "for" and the "no await in loop" here is a typescript ESLint friendly version of the above answer:
async function runPromisesSequentially<T>(promises: Array<Promise<T>>):Promise<Array<T>> {
if (promises.length === 0) return [];
const [firstElement, ...rest] = promises;
return [await firstElement, ...(await runPromisesSequentially(rest))];
}
You can then just replace Promise.all
by runPromisesSequentially
.
@lmX2015's answer is close but it's taking in promises that have already started executing.
A slight modification fixes it
export async function runPromisesSequentially<T>(functions: (() => Promise<T>)[]): Promise<T[]> {
if (functions.length === 0) {
return [];
}
const [first, ...rest] = functions;
return [await first(), ...(await runPromisesSequentially(rest))];
}
The original post asked how the poster might convert their code into a .reduce()
.
Here is one possible way that uses a .reduce()
, processes each promise one at a time before processing the next, and adheres to most modern linting rules:
try {
const results = await promises.reduce(async (acc, curr) => {
return [
...(await acc),
await this.searchAndInsert.bind(this)(curr),
];
}, []);
// we only get here if ALL promises fulfill
console.log('Success', results);
res.status(200).json({ "status": 1, "message": "Success data" });
} catch (err) {
// Will catch failure of first failed promise
console.log('Failed:', err);
res.status(200).json({ "status": 0, "message": "Failed data" });
}
Of course, this doesn't read extremely well, and if you have to do this one time in your code base, it is often likely you'll want to do it more than once. It's best to wrap up the reduce into a reusable function.
const mapAsyncSequential = async (listItems, mapFn) => {
return listItems.reduce(
async (acc, curr, i) => {
return [
...acc,
await mapFn(curr, i, list),
];
},
[],
);
}
and then you can rewrite the first code snippet with the .reduce()
line updated to use the new convenience function:
try {
const results = await mapAsyncSeqential(promises, this.searchAndInsert.bind(this));
// we only get here if ALL promises fulfill
console.log('Success', results);
res.status(200).json({ "status": 1, "message": "Success data" });
} catch (err) {
// Will catch failure of first failed promise
console.log('Failed:', err);
res.status(200).json({ "status": 0, "message": "Failed data" });
}
Considering that the original Promise.all()
strategy would typically work much faster, just some points to keep in mind:
- Processing sequentially should be limited to situations where you absolutely need it. For example, if each promise depends on the previous one finishing, or if processing all promises at the same time causes server issues.
- If your issue is with server problems due to too many requests in sequence, another slightly more complex approach is to "chunk" your array up and process each chunk sequentially, but their contents with
Promise.all()
.
- If your issue is with server problems due to too many requests in sequence, another slightly more complex approach is to "chunk" your array up and process each chunk sequentially, but their contents with
- Using a
.reduce()
has a memory cost. For small arrays, this is usually negligible, but if you're processing a large enough data set, you may want to write mutative code (for...of, etc) to ensure performance is not compromised.
async await
that will resolve your problem. – Azeem Aslam Commented Feb 25, 2019 at 13:29Promise
constructor antipattern insearchAndInsert
! – Bergi Commented Feb 25, 2019 at 13:34