Detect the end of multiple async loops [duplicate] - javascript
As far as I understand, in ES7/ES2016 putting multiple await's in code will work similar to chaining .then() with promises, meaning that they will execute one after the other rather than in parallel. So, for example, we have this code:
await someCall();
await anotherCall();
Do I understand it correctly that anotherCall() will be called only when someCall() is completed? What is the most elegant way of calling them in parallel?
I want to use it in Node, so maybe there's a solution with async library?
EDIT: I'm not satisfied with the solution provided in this question: Slowdown due to non-parallel awaiting of promises in async generators, because it uses generators and I'm asking about a more general use case.
You can await on Promise.all():
await Promise.all([someCall(), anotherCall()]);
To store the results:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Note that Promise.all fails fast, which means that as soon as one of the promises supplied to it rejects, then the entire thing rejects.
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.all([happy('happy', 100), sad('sad', 50)])
.then(console.log).catch(console.log) // 'sad'
If, instead, you want to wait for all the promises to either fulfill or reject, then you can use Promise.allSettled. Note that Internet Explorer does not natively support this method.
const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))
Promise.allSettled([happy('happy', 100), sad('sad', 50)])
.then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]
Note: If you use Promise.all actions that managed to finish before rejection happen are not rolled back, so you may need to take care of such situation. For example
if you have 5 actions, 4 quick, 1 slow and slow rejects. Those 4
actions may be already executed so you may need to roll back. In such situation consider using Promise.allSettled while it will provide exact detail which action failed and which not.
TL;DR
Use Promise.all for the parallel function calls, the answer behaviors not correctly when the error occurs.
First, execute all the asynchronous calls at once and obtain all the Promise objects. Second, use await on the Promise objects. This way, while you wait for the first Promise to resolve the other asynchronous calls are still progressing. Overall, you will only wait for as long as the slowest asynchronous call. For example:
// Begin first call and store promise without waiting
const someResult = someCall();
// Begin second call and store promise without waiting
const anotherResult = anotherCall();
// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];
// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise
JSbin example: http://jsbin.com/xerifanima/edit?js,console
Caveat: It doesn't matter if the await calls are on the same line or on different lines, so long as the first await call happens after all of the asynchronous calls. See JohnnyHK's comment.
Update: this answer has a different timing in error handling according to the #bergi's answer, it does NOT throw out the error as the error occurs but after all the promises are executed.
I compare the result with #jonny's tip: [result1, result2] = Promise.all([async1(), async2()]), check the following code snippet
const correctAsync500ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, 'correct500msResult');
});
};
const correctAsync100ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 100, 'correct100msResult');
});
};
const rejectAsync100ms = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, 'reject100msError');
});
};
const asyncInArray = async (fun1, fun2) => {
const label = 'test async functions in array';
try {
console.time(label);
const p1 = fun1();
const p2 = fun2();
const result = [await p1, await p2];
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
const asyncInPromiseAll = async (fun1, fun2) => {
const label = 'test async functions with Promise.all';
try {
console.time(label);
let [value1, value2] = await Promise.all([fun1(), fun2()]);
console.timeEnd(label);
} catch (e) {
console.error('error is', e);
console.timeEnd(label);
}
};
(async () => {
console.group('async functions without error');
console.log('async functions without error: start')
await asyncInArray(correctAsync500ms, correctAsync100ms);
await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
console.groupEnd();
console.group('async functions with error');
console.log('async functions with error: start')
await asyncInArray(correctAsync500ms, rejectAsync100ms);
await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
console.groupEnd();
})();
Update:
The original answer makes it difficult (and in some cases impossible) to correctly handle promise rejections. The correct solution is to use Promise.all:
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Original answer:
Just make sure you call both functions before you await either one:
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
There is another way without Promise.all() to do it in parallel:
First, we have 2 functions to print numbers:
function printNumber1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number1 is done");
resolve(10);
},1000);
});
}
function printNumber2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number2 is done");
resolve(20);
},500);
});
}
This is sequential:
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
This is parallel:
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
I've created a gist testing some different ways of resolving promises, with results. It may be helpful to see the options that work.
Edit: Gist content as per Jin Lee's comment
// Simple gist to test parallel promise resolution when using async / await
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
async function test() {
return [
await promiseWait(1000),
await promiseWait(5000),
await promiseWait(9000),
await promiseWait(3000),
]
}
async function test2() {
return {
'aa': await promiseWait(1000),
'bb': await promiseWait(5000),
'cc': await promiseWait(9000),
'dd': await promiseWait(3000),
}
}
async function test3() {
return await {
'aa': promiseWait(1000),
'bb': promiseWait(5000),
'cc': promiseWait(9000),
'dd': promiseWait(3000),
}
}
async function test4() {
const p1 = promiseWait(1000);
const p2 = promiseWait(5000);
const p3 = promiseWait(9000);
const p4 = promiseWait(3000);
return {
'aa': await p1,
'bb': await p2,
'cc': await p3,
'dd': await p4,
};
}
async function test5() {
return await Promise.all([
await promiseWait(1000),
await promiseWait(5000),
await promiseWait(9000),
await promiseWait(3000),
]);
}
async function test6() {
return await Promise.all([
promiseWait(1000),
promiseWait(5000),
promiseWait(9000),
promiseWait(3000),
]);
}
async function test7() {
const p1 = promiseWait(1000);
const p2 = promiseWait(5000);
const p3 = promiseWait(9000);
return {
'aa': await p1,
'bb': await p2,
'cc': await p3,
'dd': await promiseWait(3000),
};
}
let start = Date.now();
test().then((res) => {
console.log('Test Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test2().then((res) => {
console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test3().then((res) => {
console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test4().then((res) => {
console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test5().then((res) => {
console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res);
start = Date.now();
test6().then((res) => {
console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res);
});
start = Date.now();
test7().then((res) => {
console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res);
});
});
});
});
});
});
/*
Test Done, elapsed 18.006 [ true, true, true, true ]
Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true }
Test3 Done, elapsed 0 { aa: Promise { <pending> },
bb: Promise { <pending> },
cc: Promise { <pending> },
dd: Promise { <pending> } }
Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true }
Test5 Done, elapsed 18.008 [ true, true, true, true ]
Test6 Done, elapsed 9.003 [ true, true, true, true ]
Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true }
*/
In my case, I have several tasks I want to execute in parallel, but I need to do something different with the result of those tasks.
function wait(ms, data) {
console.log('Starting task:', data, ms);
return new Promise(resolve => setTimeout(resolve, ms, data));
}
var tasks = [
async () => {
var result = await wait(1000, 'moose');
// do something with result
console.log(result);
},
async () => {
var result = await wait(500, 'taco');
// do something with result
console.log(result);
},
async () => {
var result = await wait(5000, 'burp');
// do something with result
console.log(result);
}
]
await Promise.all(tasks.map(p => p()));
console.log('done');
And the output:
Starting task: moose 1000
Starting task: taco 500
Starting task: burp 5000
taco
moose
burp
done
(async function(){
function wait(ms, data) {
console.log('Starting task:', data, ms);
return new Promise(resolve => setTimeout(resolve, ms, data));
}
var tasks = [
async () => {
var result = await wait(1000, 'moose');
// do something with result
console.log(result);
},
async () => {
var result = await wait(500, 'taco');
// do something with result
console.log(result);
},
async () => {
var result = await wait(5000, 'burp');
// do something with result
console.log(result);
}
]
await Promise.all(tasks.map(p => p()));
console.log('done');
})();
await Promise.all([someCall(), anotherCall()]); as already mention will act as a thread fence (very common in parallel code as CUDA), hence it will allow all the promises in it to run without blocking each other, but will prevent the execution to continue until ALL are resolved.
another approach that is worth to share is the Node.js async that will also allow you to easily control the amount of concurrency that is usually desirable if the task is directly linked to the use of limited resources as API call, I/O operations, etc.
// create a queue object with concurrency 2
var q = async.queue(function(task, callback) {
console.log('Hello ' + task.name);
callback();
}, 2);
// assign a callback
q.drain = function() {
console.log('All items have been processed');
};
// add some items to the queue
q.push({name: 'foo'}, function(err) {
console.log('Finished processing foo');
});
q.push({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
// add some items to the queue (batch-wise)
q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
console.log('Finished processing item');
});
// add some items to the front of the queue
q.unshift({name: 'bar'}, function (err) {
console.log('Finished processing bar');
});
Credits to the Medium article autor (read more)
You can call multiple asynchronous functions without awaiting them. This will execute them in parallel. While doing so, save the returned promises in variables, and await them at some point either individually or using Promise.all() and process the results.
You can also wrap the function calls with try...catch to handle failures of individual asynchronous actions and provide fallback logic.
Here's an example:
Observe the logs, the logs printed at the beginning of execution of the individual asynchronous functions get printed immediately even though the first function takes 5 seconds to resolve.
function someLongFunc () {
return new Promise((resolve, reject)=> {
console.log('Executing function 1')
setTimeout(resolve, 5000)
})
}
function anotherLongFunc () {
return new Promise((resolve, reject)=> {
console.log('Executing function 2')
setTimeout(resolve, 5000)
})
}
async function main () {
let someLongFuncPromise, anotherLongFuncPromise
const start = Date.now()
try {
someLongFuncPromise = someLongFunc()
}
catch (ex) {
console.error('something went wrong during func 1')
}
try {
anotherLongFuncPromise = anotherLongFunc()
}
catch (ex) {
console.error('something went wrong during func 2')
}
await someLongFuncPromise
await anotherLongFuncPromise
const totalTime = Date.now() - start
console.log('Execution completed in ', totalTime)
}
main()
// A generic test function that can be configured
// with an arbitrary delay and to either resolve or reject
const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
console.log(`Done ${ delay }`);
resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
}, delay));
// Our async handler function
const handler = async () => {
// Promise 1 runs first, but resolves last
const p1 = test(10000, true);
// Promise 2 run second, and also resolves
const p2 = test(5000, true);
// Promise 3 runs last, but completes first (with a rejection)
// Note the catch to trap the error immediately
const p3 = test(1000, false).catch(e => console.log(e));
// Await all in parallel
const r = await Promise.all([p1, p2, p3]);
// Display the results
console.log(r);
};
// Run the handler
handler();
/*
Done 1000
Reject 1000
Done 5000
Done 10000
*/
Whilst setting p1, p2 and p3 is not strictly running them in parallel, they do not hold up any execution and you can trap contextual errors with a catch.
This can be accomplished with Promise.allSettled(), which is similar to Promise.all() but without the fail-fast behavior.
async function Promise1() {
throw "Failure!";
}
async function Promise2() {
return "Success!";
}
const [Promise1Result, Promise2Result] = await Promise.allSettled([Promise1(), Promise2()]);
console.log(Promise1Result); // {status: "rejected", reason: "Failure!"}
console.log(Promise2Result); // {status: "fulfilled", value: "Success!"}
Note: This is a bleeding edge feature with limited browser support, so I strongly recommend including a polyfill for this function.
I create a helper function waitAll, may be it can make it sweeter.
It only works in nodejs for now, not in browser chrome.
//const parallel = async (...items) => {
const waitAll = async (...items) => {
//this function does start execution the functions
//the execution has been started before running this code here
//instead it collects of the result of execution of the functions
const temp = [];
for (const item of items) {
//this is not
//temp.push(await item())
//it does wait for the result in series (not in parallel), but
//it doesn't affect the parallel execution of those functions
//because they haven started earlier
temp.push(await item);
}
return temp;
};
//the async functions are executed in parallel before passed
//in the waitAll function
//const finalResult = await waitAll(someResult(), anotherResult());
//const finalResult = await parallel(someResult(), anotherResult());
//or
const [result1, result2] = await waitAll(someResult(), anotherResult());
//const [result1, result2] = await parallel(someResult(), anotherResult());
I vote for:
await Promise.all([someCall(), anotherCall()]);
Be aware of the moment you call functions, it may cause unexpected result:
// Supposing anotherCall() will trigger a request to create a new User
if (callFirst) {
await someCall();
} else {
await Promise.all([someCall(), anotherCall()]); // --> create new User here
}
But following always triggers request to create new User
// Supposing anotherCall() will trigger a request to create a new User
const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User
if (callFirst) {
await someCall();
} else {
const finalResult = [await someResult, await anotherResult]
}
Related
How to wait for Promise.all() to complete before reaching next line?
I'm learning Node.js. I have to call an async function work() inside my Promise.all() loop and it must be completed before moving on to statements that are after the Promise.all(). Currently, it reaches the FINISH statment before completing work(). What is the right way to make the code wait for work() function to complete? const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = Promise.resolve(3); async function work() { await new Promise((resolve, reject) => { setTimeout(resolve, 2000, 'foo'); }) console.log('some work here') } async function main() { await Promise.all([promise1, promise2, promise3]).then((values) => { values.forEach(function(item) { console.log(item) work() }); }); console.log('FINISH') } main()
It's hard to tell what you're really after here, but don't mix and match await and then, in general. const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = Promise.resolve(3); function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function work(value) { await delay(1000 * value); console.log("some work here", value); await delay(1000); return value * 2; } async function main() { // Doesn't really do much, since these are already resolved... const values = await Promise.all([promise1, promise2, promise3]); // Pass all of those values to `work`, which returns a promise, // and wait for all of those promises to resolve. const workedValues = await Promise.all(values.map(work)); console.log(workedValues); } main(); prints out (the first lines with various delays) some work here 1 some work here 2 some work here 3 [ 2, 4, 6 ]
I think you forgot the await before calling your work() function.
Async Delay in sending data [duplicate]
As far as I understand, in ES7/ES2016 putting multiple await's in code will work similar to chaining .then() with promises, meaning that they will execute one after the other rather than in parallel. So, for example, we have this code: await someCall(); await anotherCall(); Do I understand it correctly that anotherCall() will be called only when someCall() is completed? What is the most elegant way of calling them in parallel? I want to use it in Node, so maybe there's a solution with async library? EDIT: I'm not satisfied with the solution provided in this question: Slowdown due to non-parallel awaiting of promises in async generators, because it uses generators and I'm asking about a more general use case.
You can await on Promise.all(): await Promise.all([someCall(), anotherCall()]); To store the results: let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]); Note that Promise.all fails fast, which means that as soon as one of the promises supplied to it rejects, then the entire thing rejects. const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms)) const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms)) Promise.all([happy('happy', 100), sad('sad', 50)]) .then(console.log).catch(console.log) // 'sad' If, instead, you want to wait for all the promises to either fulfill or reject, then you can use Promise.allSettled. Note that Internet Explorer does not natively support this method. const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms)) const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms)) Promise.allSettled([happy('happy', 100), sad('sad', 50)]) .then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }] Note: If you use Promise.all actions that managed to finish before rejection happen are not rolled back, so you may need to take care of such situation. For example if you have 5 actions, 4 quick, 1 slow and slow rejects. Those 4 actions may be already executed so you may need to roll back. In such situation consider using Promise.allSettled while it will provide exact detail which action failed and which not.
TL;DR Use Promise.all for the parallel function calls, the answer behaviors not correctly when the error occurs. First, execute all the asynchronous calls at once and obtain all the Promise objects. Second, use await on the Promise objects. This way, while you wait for the first Promise to resolve the other asynchronous calls are still progressing. Overall, you will only wait for as long as the slowest asynchronous call. For example: // Begin first call and store promise without waiting const someResult = someCall(); // Begin second call and store promise without waiting const anotherResult = anotherCall(); // Now we await for both results, whose async processes have already been started const finalResult = [await someResult, await anotherResult]; // At this point all calls have been resolved // Now when accessing someResult| anotherResult, // you will have a value instead of a promise JSbin example: http://jsbin.com/xerifanima/edit?js,console Caveat: It doesn't matter if the await calls are on the same line or on different lines, so long as the first await call happens after all of the asynchronous calls. See JohnnyHK's comment. Update: this answer has a different timing in error handling according to the #bergi's answer, it does NOT throw out the error as the error occurs but after all the promises are executed. I compare the result with #jonny's tip: [result1, result2] = Promise.all([async1(), async2()]), check the following code snippet const correctAsync500ms = () => { return new Promise(resolve => { setTimeout(resolve, 500, 'correct500msResult'); }); }; const correctAsync100ms = () => { return new Promise(resolve => { setTimeout(resolve, 100, 'correct100msResult'); }); }; const rejectAsync100ms = () => { return new Promise((resolve, reject) => { setTimeout(reject, 100, 'reject100msError'); }); }; const asyncInArray = async (fun1, fun2) => { const label = 'test async functions in array'; try { console.time(label); const p1 = fun1(); const p2 = fun2(); const result = [await p1, await p2]; console.timeEnd(label); } catch (e) { console.error('error is', e); console.timeEnd(label); } }; const asyncInPromiseAll = async (fun1, fun2) => { const label = 'test async functions with Promise.all'; try { console.time(label); let [value1, value2] = await Promise.all([fun1(), fun2()]); console.timeEnd(label); } catch (e) { console.error('error is', e); console.timeEnd(label); } }; (async () => { console.group('async functions without error'); console.log('async functions without error: start') await asyncInArray(correctAsync500ms, correctAsync100ms); await asyncInPromiseAll(correctAsync500ms, correctAsync100ms); console.groupEnd(); console.group('async functions with error'); console.log('async functions with error: start') await asyncInArray(correctAsync500ms, rejectAsync100ms); await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms); console.groupEnd(); })();
Update: The original answer makes it difficult (and in some cases impossible) to correctly handle promise rejections. The correct solution is to use Promise.all: const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]); Original answer: Just make sure you call both functions before you await either one: // Call both functions const somePromise = someCall(); const anotherPromise = anotherCall(); // Await both promises const someResult = await somePromise; const anotherResult = await anotherPromise;
There is another way without Promise.all() to do it in parallel: First, we have 2 functions to print numbers: function printNumber1() { return new Promise((resolve,reject) => { setTimeout(() => { console.log("Number1 is done"); resolve(10); },1000); }); } function printNumber2() { return new Promise((resolve,reject) => { setTimeout(() => { console.log("Number2 is done"); resolve(20); },500); }); } This is sequential: async function oneByOne() { const number1 = await printNumber1(); const number2 = await printNumber2(); } //Output: Number1 is done, Number2 is done This is parallel: async function inParallel() { const promise1 = printNumber1(); const promise2 = printNumber2(); const number1 = await promise1; const number2 = await promise2; } //Output: Number2 is done, Number1 is done
I've created a gist testing some different ways of resolving promises, with results. It may be helpful to see the options that work. Edit: Gist content as per Jin Lee's comment // Simple gist to test parallel promise resolution when using async / await function promiseWait(time) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(true); }, time); }); } async function test() { return [ await promiseWait(1000), await promiseWait(5000), await promiseWait(9000), await promiseWait(3000), ] } async function test2() { return { 'aa': await promiseWait(1000), 'bb': await promiseWait(5000), 'cc': await promiseWait(9000), 'dd': await promiseWait(3000), } } async function test3() { return await { 'aa': promiseWait(1000), 'bb': promiseWait(5000), 'cc': promiseWait(9000), 'dd': promiseWait(3000), } } async function test4() { const p1 = promiseWait(1000); const p2 = promiseWait(5000); const p3 = promiseWait(9000); const p4 = promiseWait(3000); return { 'aa': await p1, 'bb': await p2, 'cc': await p3, 'dd': await p4, }; } async function test5() { return await Promise.all([ await promiseWait(1000), await promiseWait(5000), await promiseWait(9000), await promiseWait(3000), ]); } async function test6() { return await Promise.all([ promiseWait(1000), promiseWait(5000), promiseWait(9000), promiseWait(3000), ]); } async function test7() { const p1 = promiseWait(1000); const p2 = promiseWait(5000); const p3 = promiseWait(9000); return { 'aa': await p1, 'bb': await p2, 'cc': await p3, 'dd': await promiseWait(3000), }; } let start = Date.now(); test().then((res) => { console.log('Test Done, elapsed', (Date.now() - start) / 1000, res); start = Date.now(); test2().then((res) => { console.log('Test2 Done, elapsed', (Date.now() - start) / 1000, res); start = Date.now(); test3().then((res) => { console.log('Test3 Done, elapsed', (Date.now() - start) / 1000, res); start = Date.now(); test4().then((res) => { console.log('Test4 Done, elapsed', (Date.now() - start) / 1000, res); start = Date.now(); test5().then((res) => { console.log('Test5 Done, elapsed', (Date.now() - start) / 1000, res); start = Date.now(); test6().then((res) => { console.log('Test6 Done, elapsed', (Date.now() - start) / 1000, res); }); start = Date.now(); test7().then((res) => { console.log('Test7 Done, elapsed', (Date.now() - start) / 1000, res); }); }); }); }); }); }); /* Test Done, elapsed 18.006 [ true, true, true, true ] Test2 Done, elapsed 18.009 { aa: true, bb: true, cc: true, dd: true } Test3 Done, elapsed 0 { aa: Promise { <pending> }, bb: Promise { <pending> }, cc: Promise { <pending> }, dd: Promise { <pending> } } Test4 Done, elapsed 9 { aa: true, bb: true, cc: true, dd: true } Test5 Done, elapsed 18.008 [ true, true, true, true ] Test6 Done, elapsed 9.003 [ true, true, true, true ] Test7 Done, elapsed 12.007 { aa: true, bb: true, cc: true, dd: true } */
In my case, I have several tasks I want to execute in parallel, but I need to do something different with the result of those tasks. function wait(ms, data) { console.log('Starting task:', data, ms); return new Promise(resolve => setTimeout(resolve, ms, data)); } var tasks = [ async () => { var result = await wait(1000, 'moose'); // do something with result console.log(result); }, async () => { var result = await wait(500, 'taco'); // do something with result console.log(result); }, async () => { var result = await wait(5000, 'burp'); // do something with result console.log(result); } ] await Promise.all(tasks.map(p => p())); console.log('done'); And the output: Starting task: moose 1000 Starting task: taco 500 Starting task: burp 5000 taco moose burp done (async function(){ function wait(ms, data) { console.log('Starting task:', data, ms); return new Promise(resolve => setTimeout(resolve, ms, data)); } var tasks = [ async () => { var result = await wait(1000, 'moose'); // do something with result console.log(result); }, async () => { var result = await wait(500, 'taco'); // do something with result console.log(result); }, async () => { var result = await wait(5000, 'burp'); // do something with result console.log(result); } ] await Promise.all(tasks.map(p => p())); console.log('done'); })();
await Promise.all([someCall(), anotherCall()]); as already mention will act as a thread fence (very common in parallel code as CUDA), hence it will allow all the promises in it to run without blocking each other, but will prevent the execution to continue until ALL are resolved. another approach that is worth to share is the Node.js async that will also allow you to easily control the amount of concurrency that is usually desirable if the task is directly linked to the use of limited resources as API call, I/O operations, etc. // create a queue object with concurrency 2 var q = async.queue(function(task, callback) { console.log('Hello ' + task.name); callback(); }, 2); // assign a callback q.drain = function() { console.log('All items have been processed'); }; // add some items to the queue q.push({name: 'foo'}, function(err) { console.log('Finished processing foo'); }); q.push({name: 'bar'}, function (err) { console.log('Finished processing bar'); }); // add some items to the queue (batch-wise) q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) { console.log('Finished processing item'); }); // add some items to the front of the queue q.unshift({name: 'bar'}, function (err) { console.log('Finished processing bar'); }); Credits to the Medium article autor (read more)
You can call multiple asynchronous functions without awaiting them. This will execute them in parallel. While doing so, save the returned promises in variables, and await them at some point either individually or using Promise.all() and process the results. You can also wrap the function calls with try...catch to handle failures of individual asynchronous actions and provide fallback logic. Here's an example: Observe the logs, the logs printed at the beginning of execution of the individual asynchronous functions get printed immediately even though the first function takes 5 seconds to resolve. function someLongFunc () { return new Promise((resolve, reject)=> { console.log('Executing function 1') setTimeout(resolve, 5000) }) } function anotherLongFunc () { return new Promise((resolve, reject)=> { console.log('Executing function 2') setTimeout(resolve, 5000) }) } async function main () { let someLongFuncPromise, anotherLongFuncPromise const start = Date.now() try { someLongFuncPromise = someLongFunc() } catch (ex) { console.error('something went wrong during func 1') } try { anotherLongFuncPromise = anotherLongFunc() } catch (ex) { console.error('something went wrong during func 2') } await someLongFuncPromise await anotherLongFuncPromise const totalTime = Date.now() - start console.log('Execution completed in ', totalTime) } main()
// A generic test function that can be configured // with an arbitrary delay and to either resolve or reject const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => { console.log(`Done ${ delay }`); resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`) }, delay)); // Our async handler function const handler = async () => { // Promise 1 runs first, but resolves last const p1 = test(10000, true); // Promise 2 run second, and also resolves const p2 = test(5000, true); // Promise 3 runs last, but completes first (with a rejection) // Note the catch to trap the error immediately const p3 = test(1000, false).catch(e => console.log(e)); // Await all in parallel const r = await Promise.all([p1, p2, p3]); // Display the results console.log(r); }; // Run the handler handler(); /* Done 1000 Reject 1000 Done 5000 Done 10000 */ Whilst setting p1, p2 and p3 is not strictly running them in parallel, they do not hold up any execution and you can trap contextual errors with a catch.
This can be accomplished with Promise.allSettled(), which is similar to Promise.all() but without the fail-fast behavior. async function Promise1() { throw "Failure!"; } async function Promise2() { return "Success!"; } const [Promise1Result, Promise2Result] = await Promise.allSettled([Promise1(), Promise2()]); console.log(Promise1Result); // {status: "rejected", reason: "Failure!"} console.log(Promise2Result); // {status: "fulfilled", value: "Success!"} Note: This is a bleeding edge feature with limited browser support, so I strongly recommend including a polyfill for this function.
I create a helper function waitAll, may be it can make it sweeter. It only works in nodejs for now, not in browser chrome. //const parallel = async (...items) => { const waitAll = async (...items) => { //this function does start execution the functions //the execution has been started before running this code here //instead it collects of the result of execution of the functions const temp = []; for (const item of items) { //this is not //temp.push(await item()) //it does wait for the result in series (not in parallel), but //it doesn't affect the parallel execution of those functions //because they haven started earlier temp.push(await item); } return temp; }; //the async functions are executed in parallel before passed //in the waitAll function //const finalResult = await waitAll(someResult(), anotherResult()); //const finalResult = await parallel(someResult(), anotherResult()); //or const [result1, result2] = await waitAll(someResult(), anotherResult()); //const [result1, result2] = await parallel(someResult(), anotherResult());
I vote for: await Promise.all([someCall(), anotherCall()]); Be aware of the moment you call functions, it may cause unexpected result: // Supposing anotherCall() will trigger a request to create a new User if (callFirst) { await someCall(); } else { await Promise.all([someCall(), anotherCall()]); // --> create new User here } But following always triggers request to create new User // Supposing anotherCall() will trigger a request to create a new User const someResult = someCall(); const anotherResult = anotherCall(); // ->> This always creates new User if (callFirst) { await someCall(); } else { const finalResult = [await someResult, await anotherResult] }
Promise.all Replacement?
Is there any way where I can call multiple async functions in Javascript, but get response of the call as soon as one completes, unlike Promise.all that waits for all async calls to complete? What I want is, run async calls in parallel, and get response of a respective call as soon as it gets completed while other calls are still running and then update the react state. I am already using Promise.all for multiple async calls, but it gives responses when all calls are finished.
You can just iterate over an array of promises and change the state on each promise resolution: let state = null; const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve({p1: 1}); }, 2000); }); const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve({p2: 2}); }, 1000); }); const p3 = new Promise((resolve, reject) => { setTimeout(() => { resolve({p3: 3}); }, 3000); }); // Iterate promises [p1, p2, p3].forEach(p => { p.then(res => { state = Object.assign({}, state, res); console.log(state); }); }); console.log(state); You could create your own Promise method for this task: if (!Promise.each) { Promise.each = function(promises, callback, error) { if (promises && Array.isArray(promises)) { promises.forEach(p => { p.then(res => { callback(res); }, err => { if (error) { error(err); } else { console.log(err); } }); }); } } } // Usage Promise.each([p1, p2, p3], updateState);
In your question the phrase "than update ... state" is the key. You plan to update the state in such a way that as soon as one promise "completed" the corresponding state is updated. You have been trying something like this async function f1(){ await Promise.resolve(); } async funciton f2(){ await Promise.resolve(); } async function fn(){ await Promise.resolve(); } async function drvAsync(){ const [r1, r2, rn] = await Promise.all([f1(), f2(), fn()]); u1(r1); u2(r2); un(rn); } where f[n] is an async business function, u[n] is a method to deal with the result from it. This schema is not acceptable in your scenario. Perhaps fn completes faster than others and you want to update N-th state earlier. My recommendation is use no synchronization primitives at all. Instead you should deal with the results separately. function drv(){ f1().then((r1)=>u1(r1)).catch((e)=>er(e)); f2().then((r2)=>u2(r2)).catch((e)=>er(e)); fn().then((rn)=>un(rn)).catch((e)=>er(e));; } This schema will call each update (u[n]) method without waiting results from others.
This is what Promise.race is for: The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise. For instance: function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function main() { const promises = [ sleep(1000).then(() => "world"), sleep(100).then(() => "hello"), ]; const promisesWithIndices = promises.map((p, index) => p.then((result) => ({result, index})) ); while (promisesWithIndices.length > 0) { const {result, index} = await Promise.race(promisesWithIndices); console.log("%s -> %s", index, result); promisesWithIndices.splice(index, 1); } } main(); // prints "1 -> hello"; then prints "0 -> world" after a while
As long as you don't use async/await you can do the calls in parallel. Note that browsers have connection limit for network requests so if you execute for example 100 calls it will not be parallel but rather be queued. You need a callback to execute when the promise resolved. If this callback is the same for all functions than just loop all calls, create the promise and resolve with the same function. function resolved(data) { // ... } const promise1 = new Promise(function(resolve, reject) { resolve('Success!'); }).then(resolved); const promise2 = new Promise(function(resolve, reject) { resolve('Success!'); }).then(resolved);
Break for-of loop when promise never resolves [duplicate]
I'm with Node.js and TypeScript and I'm using async/await. This is my test case: async function doSomethingInSeries() { const res1 = await callApi(); const res2 = await persistInDB(res1); const res3 = await doHeavyComputation(res1); return 'simle'; } I'd like to set a timeout for the overall function. I.e. if res1 takes 2 seconds, res2 takes 0.5 seconds, res3 takes 5 seconds I'd like to have a timeout that after 3 seconds let me throw an error. With a normal setTimeout call is a problem because the scope is lost: async function doSomethingInSeries() { const timerId = setTimeout(function() { throw new Error('timeout'); }); const res1 = await callApi(); const res2 = await persistInDB(res1); const res3 = await doHeavyComputation(res1); clearTimeout(timerId); return 'simle'; } And I cannot catch it with normal Promise.catch: doSomethingInSeries().catch(function(err) { // errors in res1, res2, res3 will be catched here // but the setTimeout thing is not!! }); Any ideas on how to resolve?
You can use Promise.race to make a timeout: Promise.race([ doSomethingInSeries(), new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 11.5e3)) ]).catch(function(err) { // errors in res1, res2, res3 and the timeout will be caught here }) You cannot use setTimeout without wrapping it in a promise.
Ok I found this way: async function _doSomethingInSeries() { const res1 = await callApi(); const res2 = await persistInDB(res1); const res3 = await doHeavyComputation(res1); return 'simle'; } async function doSomethingInSeries(): Promise<any> { let timeoutId; const delay = new Promise(function(resolve, reject){ timeoutId = setTimeout(function(){ reject(new Error('timeout')); }, 1000); }); // overall timeout return Promise.race([delay, _doSomethingInSeries()]) .then( (res) => { clearTimeout(timeoutId); return res; }); } Anyone errors? The things that smells a bit to me is that using Promises as asynchronicity strategy will send us to allocate too many object that some other strategy needs but this is off-topic.
Problem with #Bergi answer that doSomethingInSeries continues executing even if you already rejected the promise. It is much better to cancel it. LATEST ANSWER You can try use AbortController for that. Check the old answer to see how to use it - api is similar. Keep in mind that task is not cancelled immediately, so continuation (awaiting, then or catch) is not called exactly after timeout. To guarantee that you can combine this and #Bergi approach. OLD ANSWER This is how it should look like: async const doSomethingInSeries = (cancellationToken) => { cancellationToken.throwIfCancelled(); const res1 = await callApi(); cancellationToken.throwIfCancelled(); const res2 = await persistInDB(res1); cancellationToken.throwIfCancelled(); const res3 = await doHeavyComputation(res1); cancellationToken.throwIfCancelled(); return 'simle'; } Here is simple implementation: const makeCancellationToken = (tag) => { let cancelled = false; return { isCancelled: () => cancelled, cancel: () => { cancelled = true; }, throwIfCancelled: () => { if (cancelled) { const error = new Error(`${tag ?? 'Task'} cancelled`); error.cancelled = true; throw error; } } } } And finally usage: const cancellationToken = makeCancellationToken('doSomething') setTimeout(cancellationToken.cancel, 5000); try { await doSomethingInSeries(cancellationToken); } catch (error) { if (error.cancelled) { // handle cancellation } } Keep in mind that task is not cancelled immediately, so continuation (awaiting, then or catch) is not called exactly after 5 secs. To guarantee that you can combine this and #Bergi approach.
Limit concurrency of pending promises
I'm looking for a promise function wrapper that can limit / throttle when a given promise is running so that only a set number of that promise is running at a given time. In the case below delayPromise should never run concurrently, they should all run one at a time in a first-come-first-serve order. import Promise from 'bluebird' function _delayPromise (seconds, str) { console.log(str) return Promise.delay(seconds) } let delayPromise = limitConcurrency(_delayPromise, 1) async function a() { await delayPromise(100, "a:a") await delayPromise(100, "a:b") await delayPromise(100, "a:c") } async function b() { await delayPromise(100, "b:a") await delayPromise(100, "b:b") await delayPromise(100, "b:c") } a().then(() => console.log('done')) b().then(() => console.log('done')) Any ideas on how to get a queue like this set up? I have a "debounce" function from the wonderful Benjamin Gruenbaum. I need to modify this to throttle a promise based on it's own execution and not the delay. export function promiseDebounce (fn, delay, count) { let working = 0 let queue = [] function work () { if ((queue.length === 0) || (working === count)) return working++ Promise.delay(delay).tap(function () { working-- }).then(work) var next = queue.shift() next[2](fn.apply(next[0], next[1])) } return function debounced () { var args = arguments return new Promise(function (resolve) { queue.push([this, args, resolve]) if (working < count) work() }.bind(this)) } }
I don't think there are any libraries to do this, but it's actually quite simple to implement yourself: function sequential(fn) { // limitConcurrency(fn, 1) let q = Promise.resolve(); return function(x) { const p = q.then(() => fn(x)); q = p.reflect(); return p; }; } For multiple concurrent requests it gets a little trickier, but can be done as well. function limitConcurrency(fn, n) { if (n == 1) return sequential(fn); // optimisation let q = Promise.resolve(); const active = new Set(); const fst = t => t[0]; const snd = t => t[1]; return function(x) { function put() { const p = fn(x); const a = p.reflect().then(() => { active.delete(a); }); active.add(a); return [Promise.race(active), p]; } if (active.size < n) { const r = put() q = fst(t); return snd(t); } else { const r = q.then(put); q = r.then(fst); return r.then(snd) } }; } Btw, you might want to have a look at the actors model and CSP. They can simplify dealing with such things, there are a few JS libraries for them out there as well. Example import Promise from 'bluebird' function sequential(fn) { var q = Promise.resolve(); return (...args) => { const p = q.then(() => fn(...args)) q = p.reflect() return p } } async function _delayPromise (seconds, str) { console.log(`${str} started`) await Promise.delay(seconds) console.log(`${str} ended`) return str } let delayPromise = sequential(_delayPromise) async function a() { await delayPromise(100, "a:a") await delayPromise(200, "a:b") await delayPromise(300, "a:c") } async function b() { await delayPromise(400, "b:a") await delayPromise(500, "b:b") await delayPromise(600, "b:c") } a().then(() => console.log('done')) b().then(() => console.log('done')) // --> with sequential() // $ babel-node test/t.js // a:a started // a:a ended // b:a started // b:a ended // a:b started // a:b ended // b:b started // b:b ended // a:c started // a:c ended // b:c started // done // b:c ended // done // --> without calling sequential() // $ babel-node test/t.js // a:a started // b:a started // a:a ended // a:b started // a:b ended // a:c started // b:a ended // b:b started // a:c ended // done // b:b ended // b:c started // b:c ended // done
Use the throttled-promise module: https://www.npmjs.com/package/throttled-promise var ThrottledPromise = require('throttled-promise'), promises = [ new ThrottledPromise(function(resolve, reject) { ... }), new ThrottledPromise(function(resolve, reject) { ... }), new ThrottledPromise(function(resolve, reject) { ... }) ]; // Run promises, but only 2 parallel ThrottledPromise.all(promises, 2) .then( ... ) .catch( ... );
I have the same problem. I wrote a library to implement it. Code is here. I created a queue to save all the promises. When you push some promises to the queue, the first several promises at the head of the queue would be popped and running. Once one promise is done, the next promise in the queue would also be popped and running. Again and again, until the queue has no Task. You can check the code for details. Hope this library would help you.
Advantages you can define the amount of concurrent promises (near simultaneous requests) consistent flow: once one promise resolve, another request start no need to guess the server capability robust against data choke, if the server stop for a moment, it will just wait, and next tasks will not start just because the clock allowed do not rely on a 3rd party module it is Vanila node.js 1st thing is to make https a promise, so we can use wait to retrieve data (removed from the example) 2nd create a promise scheduler that submit another request as any promise get resolved. 3rd make the calls Limiting requests taking by limiting the amount of concurrent promises const https = require('https') function httpRequest(method, path, body = null) { const reqOpt = { method: method, path: path, hostname: 'dbase.ez-mn.net', headers: { "Content-Type": "application/json", "Cache-Control": "no-cache" } } if (method == 'GET') reqOpt.path = path + '&max=20000' if (body) reqOpt.headers['Content-Length'] = Buffer.byteLength(body); return new Promise((resolve, reject) => { const clientRequest = https.request(reqOpt, incomingMessage => { let response = { statusCode: incomingMessage.statusCode, headers: incomingMessage.headers, body: [] }; let chunks = "" incomingMessage.on('data', chunk => { chunks += chunk; }); incomingMessage.on('end', () => { if (chunks) { try { response.body = JSON.parse(chunks); } catch (error) { reject(error) } } console.log(response) resolve(response); }); }); clientRequest.on('error', error => { reject(error); }); if (body) { clientRequest.write(body) } clientRequest.end(); }); } const asyncLimit = (fn, n) => { const pendingPromises = new Set(); return async function(...args) { while (pendingPromises.size >= n) { await Promise.race(pendingPromises); } const p = fn.apply(this, args); const r = p.catch(() => {}); pendingPromises.add(r); await r; pendingPromises.delete(r); return p; }; }; // httpRequest is the function that we want to rate the amount of requests // in this case, we set 8 requests running while not blocking other tasks (concurrency) let ratedhttpRequest = asyncLimit(httpRequest, 8); // this is our datase and caller let process = async () => { patchData=[ {path: '/rest/slots/80973975078587', body:{score:3}}, {path: '/rest/slots/809739750DFA95', body:{score:5}}, {path: '/rest/slots/AE0973750DFA96', body:{score:5}}] for (let i = 0; i < patchData.length; i++) { ratedhttpRequest('PATCH', patchData[i].path, patchData[i].body) } console.log('completed') } process()
The classic way of running async processes in series is to use async.js and use async.series(). If you prefer promise based code then there is a promise version of async.js: async-q With async-q you can once again use series: async.series([ function(){return delayPromise(100, "a:a")}, function(){return delayPromise(100, "a:b")}, function(){return delayPromise(100, "a:c")} ]) .then(function(){ console.log(done); }); Running two of them at the same time will run a and b concurrently but within each they will be sequential: // these two will run concurrently but each will run // their array of functions sequentially: async.series(a_array).then(()=>console.log('a done')); async.series(b_array).then(()=>console.log('b done')); If you want to run b after a then put it in the .then(): async.series(a_array) .then(()=>{ console.log('a done'); return async.series(b_array); }) .then(()=>{ console.log('b done'); }); If instead of running each sequentially you want to limit each to run a set number of processes concurrently then you can use parallelLimit(): // Run two promises at a time: async.parallelLimit(a_array,2) .then(()=>console.log('done')); Read up the async-q docs: https://github.com/dbushong/async-q/blob/master/READJSME.md