3 ms·
>Your "legible" example is only legible because you are not doing anything actually important like error handling and logging. async function doStuff() {
by Klathmon 7y ago
>Your "legible" example is only legible because you are not doing anything actually important like error handling and logging.
async function doStuff() {
try {
// Fetch things concurrently
const [
foos,
bars,
bazs,
] = await Promise.all([
getFoos().then((res) => {
console.log(`getFoos done with ${res}`
return res
})),
getBars(),
getBazs()
.catch((err) => {
// handle only the error in getBazs
}),
]);
// Do stuff with them...
} catch (err) {
// handle any errors
}
}
it still isn't what i'd call "pretty" code, but it's simpler for me to read and comprehend than doing most of those things without async/await or Promise.all or other helpers like that.
- koolba 7y agoI was about to write up that exact example of the nested handlers.
- Klathmon 7y agoI actually love slinging promises like that, but I tend to avoid it in most cases as there are MANY devs that would be somewhat rightfully frustrated at the code I just wrote. As always, who you are working with matters a LOT, and if I were on a team where a good portion of the devs weren't comfortable with that style of code, then I'd use more verbose ways of doing it that are more inline with what they are expecting. But on the rare occasion that the complexity necessitates the advanced promise stuff, or in jobs (like my current one) where all of us devs are super comfortable with promises being used like this, then it's a dream to work with them!
- gbuk2013 7y ago(see below)
- Klathmon 7y agoBut those 2 examples aren't doing the same thing. My Promise.all example will fire off the getFoos, getBars, and getBazs requests all at the same time, and then do things at different moments with the results in some cases. Yours will do them serially like this: getFoos -> wait for it to resolve -> run getBars -> wait for it to resolve -> run getBazs -> wait for it to resolve -> run doStuff And if individually handled errors is what you are after ala go, then this also does it while preserving the parallelism: async function doStuff() { // Fetch things concurrently const [ foos, bars, bazs, ] = await Promise.all([ getFoos().catch((err) => { // handle only the error in getFoos }), getBars().catch((err) => { // handle only the error in getBars }), getBazs().catch((err) => { // handle only the error in getBazs }), ]); // Do stuff with them... } And I'm not suggesting we always use promises in every area, but that for some things they are the perfect tool for the job. There are some things that promises are really bad at that callbacks do really well. One example is progress systems. A callback can be called multiple times over the course of an async call with percentage it's completed every call. A promise is one-and-done, and that's a big issue in many areas. But that's why callbacks aren't really "deprecated" as much as they are relegated to doing what they do best, while leaving things that Promises and async/await do best to them.
- gbuk2013 7y agoYou are right - got mixed up in the responses - serves me right for trying to do too many things async! I would do it like this (if I didn't have access to "async/await" which is sadly the case for me in Node sometimes): function doStuff (foos, bars, bazs, callback) { // do stuff } function getStuff (callback) { let foos, bars, bazs; let semaphore = 3; function collector () { if (--semaphore) { return; } doStuff(foo, bars, bazs, callback); } getFoos((err, res) => { if (err) { return callback(err); } foos = res; collector(); }); getBars((err, res) => { if (err) { return callback(err); } bars = res; collector(); }); getBazs((err, res) => { if (err) { return callback(err); } bazs = res; collector(); }); } It is more lines of code but is boringly simple to read and allows for fully custom error handling (an example of the semaphore + collector pattern I mentioned below in another comment). The custom error handling would be in the individual "get" functions which allows for easier testing. The semaphore in the collector is the most tricky part and I would possibly replace it here with an "if" to check that all the variables have a value - depends on what sort of data I could get back.