Run A Callback After Multiple Function Have Completed
Solution 1:
For handling asynchronous functions easily, the best way is to use promises
, and async/await
function thisTakes2Seconds() {
return new Promise(resolve => setTimeout(() => resolve(3), 200)); // 0.2 to avoid waiting :P
}
function thisTakes5Seconds() {
return new Promise(resolve => setTimeout(() => resolve(5), 500));
}
async function foo() {
const data = {};
data.x = await thisTakes2Seconds();
data.y = await thisTakes5Seconds();
// This will run once both promises have been resolved
console.log(data);
}
foo()
.then(() => console.log('done!')
.catch(err => console.error(err));
If you wish to perform both functions in parallel, you can do so, and wait for both to finish using Promise.all
async function foo() {
const data = {};
// Promise.all returns an array where each item is the resolved
// value of the promises passed to it, maintaining the order
// So we use destructuring to assign those values
[data.x, data.y] = await Promise.all([
thisTakes2Seconds(),
thisTakes5Seconds()
]);
console.log(data);
}
If you already have an async function using callbacks, you can easily convert it to promises.
function myAsyncFunction(callback) {
setTimeout(() => {
callback(Math.random());
}, 200);
}
function myAsyncFunctionPromise() {
return new Promise((resolve, reject) => {
myAsyncFunction(resolve);
// If there is an error callback, just pass reject too.
});
}
There are libraries like bluebird, that already have an utility method to promisify callback API.
http://bluebirdjs.com/docs/api/promise.promisify.html
If you're running it on the browser, and need to support outdated ones, you can use babel to transpile async/await
to ES5
Solution 2:
Your thisTakesXSeconds
functions are immediately returning their results. That tells us that they're synchronous. No need for callbacks, that code will just take ~7 seconds to run.
If thisTakesXSeconds
started an asynchronous process that took X seconds (although the fact you're return a result suggests otherwise), we'd look at ways of managing the completion process.
am I really supposed to have nested callbacks several functions deep?
That question, and the general dissatisfaction with the answer "yes," is why we now have promises and even async
functions. :-)
You'd make your thisTakesXSeconds
functions return a promise, and then do something along these lines if the functions can run in parallel:
Promise.all([
thisTakes2Seconds(),
thisTakes5Seconds()
])
.then(([x, y]) => {
data.x = x;
data.y = y;
// use or return `data` here
})
// return the promise or add a `catch` handler
If they need to run in series (one after another), then
thisTakes2Seconds()
.then(x => {
data.x = x;
return thisTakes5Seconds();
})
.then(y => {
data.y = y;
// use or return `data` here
})
// return the promise or add a `catch` handler
...which looks a bit clearer in an async
function:
data.x = await thisTakes2Seconds();
data.y = await thisTakes5Seconds();
// use or return `data` here
// add appropriate error handling (at this level or when calling the function)
Solution 3:
One technique I have used to handle executing some code after several async calls have executed is to use a "has completed" counter or object.
Each function executes a callback that includes
if (counter == numberOfFuntionsIWantedToComplete)
doTheAfterWeHaveAllDataThing`
Post a Comment for "Run A Callback After Multiple Function Have Completed"