I like to run a set of functions in parallel and when all are done, I like to run some final stuff. My JavaScript looks like this:
const logger = console;
const functionOne = function () {
logger.info("Starting functionOne");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
}, 20000);
});
};
const functionTwo = function () {
logger.info("Starting functionTwo");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionTwo after 10 sec.");
}, 10000);
});
};
const runningFunctions = async function () {
logger.info('Start jobs');
functionOne();
functionTwo();
}
runningFunctions();
logger.info(`All done after 20 sec.`);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
In principle my log output should be this one:
2021-11-24 16:54:31.111 [info] -> Start jobs
2021-11-24 16:54:31.112 [info] -> Starting functionOne
2021-11-24 16:54:31.113 [info] -> Starting functionTwo
2021-11-24 16:54:41.115 [info] -> Finished functionTwo after 10 sec.
2021-11-24 16:54:51.115 [info] -> Finished functionOne after 20 sec.
2021-11-24 16:54:51.116 [info] -> All done after 20 sec.
I tried different solution, e.g.
runningFunctions().then(
() => { logger.info(`All done after 20 sec.`) }
).catch(
err => { logger.error(err) }
);
or
Promise.all([functionOne(), functionTwo()]).then(() => {
logger.info(`All done after 20 sec.`);
});
and many others, but none of them worked as expected. In most cases message All done after 20 sec.
comes immediately after functionOne/functionTwo started or the final log is not printed at all.
How do I have to write the script?
CodePudding user response:
New in ES2020 is Promise.allSettled
.
The
Promise.allSettled()
method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
This is slightly different to Promise.all
which rejects immediately if any of the promises are rejected.
function getData(count) {
return new Promise((res, rej) => {
setTimeout(() => {
if (count !== 2) res(count);
rej(count);
}, 1000);
});
}
async function main() {
const promises = [getData(1), getData(2), getData(3)];
const response = await Promise.allSettled(promises);
console.log(response);
}
main();
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
First, you have to fix both functionOne()
and functionTwo()
so that they resolve()
the promise they create when their timer fires. Without that, they just create a promise that never resolves which isn't very useful and does not notify the caller when they are done.
Then, to run them in parallel, use Promise.all()
which will let you call both functions and then it will track both returned promises together and the promise returned from the call to Promise.all()
will resolve when both functions have completed or reject if either one of the functions rejected their promise.
If your example here, none of your promises rejects, but if you want to know when all promises have finished even if some reject, then you would use Promise.allSettled()
instead of Promise.all()
. The main difference is that Promise.all()
will short-circuit and reject it's promise as soon as any promise you pass it rejects, whereas Promise.allSettled()
will wait until all promises are done, regardless of resolve/reject. Though you aren't using it here, the resolved value from Promise.allSettled()
is different also so that you can tell which promises rejected and which resolved.
Here's a runnable (in the snippet) example that uses Promise.all()
.
You can swap in Promise.allSettled()
if that's the behavior you'd rather see:
const logger = console;
const functionOne = function () {
logger.info("Starting functionOne");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
resolve();
}, 20000);
});
};
const functionTwo = function () {
logger.info("Starting functionTwo");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionTwo after 10 sec.");
resolve();
}, 10000);
});
};
const runningFunctions = function () {
logger.info('Start jobs');
return Promise.all([functionOne(), functionTwo()]);
}
runningFunctions().then(() => {
logger.info(`All done after 20 sec.`);
}).catch(err => {
console.log(err);
});
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Note that if you run the snippet, you can see the actual timing of each log statement to verify that things happen in the appropriate time.
To run the two functions in sequence, you can just await
each function call. Here's a runnable (in the snippet) example:
const logger = console;
const functionOne = function () {
logger.info("Starting functionOne");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
resolve();
}, 20000);
});
};
const functionTwo = function () {
logger.info("Starting functionTwo");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionTwo after 10 sec.");
resolve();
}, 10000);
});
};
const runningFunctions = async function () {
logger.info('Start jobs');
await functionOne();
await functionTwo();
}
runningFunctions().then(() => {
logger.info(`All done after 30 sec.`);
}).catch(err => {
console.log(err);
});
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Maybe you need Promise.all()
?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
CodePudding user response:
In your example, I noticed that you won't resolve the Promise:
Instead of
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
}, 20000);
Try
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
resolve(true); // or any value
}, 20000);
CodePudding user response:
Promise.all
is the way to go, but you'd need to resolve
your promises
const logger = console;
const functionOne = function () {
logger.info("Starting functionOne");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
resolve(); // need to resolve
}, 20000);
});
};
const functionTwo = function () {
logger.info("Starting functionTwo");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionTwo after 10 sec.");
resolve();
}, 10000);
});
};
Promise.all([functionOne(), functionTwo()]).then(() => {
logger.info(`All done after 20 sec.`);
});
CodePudding user response:
Better to share a jsfiddle /working code for quicker debugging. Here is a working solution for your ref.
Hope below JS fiddle helps. https://jsfiddle.net/xqjvtypb/
const functionOne = function () {
logger.info("Starting functionOne");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionOne after 20 sec.");
}, 20000);
});
};
const functionTwo = function () {
logger.info("Starting functionTwo");
return new Promise(resolve => {
setTimeout(function () {
logger.info("Finished functionTwo after 10 sec.");
}, 10000);
});
};
const runningFunctions = async function () {
logger.info('Start jobs');
Promise.all([functionOne(), functionTwo()]).then(() => {
logger.info(`All done after 20 sec.`);
});
/* functionOne() */;
/* functionTwo() */;
}
runningFunctions().then(()=>{
// No action required.
});
CodePudding user response:
Your main function should look like that:
const runningFunctions = async function () {
logger.info('Start jobs');
await functionOne();
await functionTwo();
logger.info(`All done after 20 sec.`);
}
If you wan't wait until another function will resolve you need to use async/await
More here