I mostly encourage avoiding async-await. The async-await
abstraction is an abstraction over an abstraction and disguises nested then
s under an imperative looking skin. You can simply do as follows
Promise.resolve(10)
.then(r => r > 9 ? Promise.all([Promise.resolve(11), Promise.resolve(12)]) : [r])
.then(([p,q]) => q ? console.log(p, q) : console.log(p));
Edit:
There are some comments I need to address;
Promises provide a clear way to encapsulate asynchronous code and isolate it from synchronous code without creating a nested structure. On the other hand, async-await does create a nested structure similar to calling .then() inside a previous .then(), but hides it. This can be useful when you need to access resolutions from outer scopes (previous then stages). In Promises, you need to explicitly carry previous resolutions to the next then stage if needed, as demonstrated in the example above. Although this may seem like extra work, it is actually a very safe and functional way to chain asynchronous tasks. Such as a simple looking async-await
code as;
(async function(){
let a = await Promise.resolve(10),
b = await Promise.resolve(2),
c = await Promise.resolve(4);
console.log(a+b+c);
})();
when done with Promises would be like;
function x(){
Promise.resolve(10)
.then(a => Promise.resolve(2)
.then(b => Promise.resolve(4)
.then(c => console.log(a+b+c))));
}
You may say that it looks better with async-await
but I believe that's not how you should handle the async code and never with the promises as shown above. But all you do with async-await
is this. While it resembles the sync code, it also bears the potential to quickly escalate into a mess if you are using async code alongside sync code.
I'm not saying that you should never use async-await
, but if you do, you must first understand how your code would look if implemented with Promises. Many novice coders get lost while using async-await
and spread random awaits throughout their code or create unnecessary async functions (often in the form of async callbacks).
Another point to consider is that async functions must return Promises, while normal functions can return Promises or synchronous values. By using Promises in normal functions, you have the option to switch to an async timeline or not by relying on a condition.
Promises are also open to constructing much more sophisticated async workflows by employing their resolve
and reject
functions (functional programming here..!) outside the Promise
constructor. Promises are probably as close as JavaScript can get to a functional programming language. Unfortunately, it seems that Promises have been shelved with the introduction of async-await
abstraction.
Having said that, since the introduction of top-level await, I also find myself using await
or the for await of
loops instead of recursion, etc. However, deeper in the code, I mostly prefer Promises.
At the end of the day, I feel like only at gun point I shall attempt to debug a novice asynchronous code with randomly spread asyncs and awaits. Otherwise it's your decision. Use the one that you find yourself most comfortable to live with.