1. Preface
So far in front-end development, asynchronous problems have experienced the despair of Callback Hell, the normative melee of Promise/Deffered, the invincibility of Generator, and now the acceptance of Async/Await by the public, in which Promise and Async/Await are still active in code. Their perceptions and evaluations have been reversed many times, and they have their own fans, creating a love-hate relationship that continues to this day, and the thoughts and inspirations behind them are still worth pondering.
Advance notice.
The goal of this article is not to start a debate, nor to promote either approach as the only best practice for front-end asynchrony, but to explore the hidden consensus underneath the controversy, based on an introduction to the knowledge and anecdotes behind Promise and Async/Await.
2. Promise
Promise is a solution for asynchronous programming that is more sensible and powerful than the traditional callback hell.
A Promise is simply a container that stores the result of a time that will only end in the future. Syntactically, a Promise is an object from which messages for asynchronous operations can be retrieved, and it provides a unified API so that all kinds of asynchronous operations can be handled in the same way. Its internal state is as follows.
The flow between states is irreversible and the code is written as follows.
|
|
It is easier to understand from a syntactic point of view, but the beauty is the lack of brevity, the inability to break points and the redundant anonymous functions.
2.1. How is a Promise implemented?
When I first started out, I researched the topic “How to implement a Promise” and tried to write the following code.
|
|
Although this code sucks, I have to say that it was quite satisfying at the time, and later it turned out that it could not solve the problem of asynchronous registration.
|
|
Those interested in this section can look up the standard implementation for themselves, but the process of exploring it really does evoke an interest in the basics, which is the reason this article went digging into it in the first place.
Next, let’s look at Async/Await.
3. Async/Await
Async/Await is not a new concept, and indeed it is.
It was formally introduced in 2012 with the release of version 5.0 of Microsoft’s C# language, and then in Python and Scala. After that, the Async/Await specification was formally introduced in ES 2016, which is the subject of our discussion today.
Here is a sample code for using Async/Await in C#.
|
|
And look at how it is used in JavaScript.
In fact, there are quite a few Async/Await-like implementations in the front-end domain
3.1. How is Async/Await implemented?
The ES2017 standard introduced the async function to make asynchronous operations more convenient.
Here is a quote from Mr. Yifeng Ruan’s description.
What is the async function? In a nutshell, it’s syntactic sugar for the Generator function.
The preceding section has a Generator function that reads two files in turn.
|
|
The function gen
in the above code could be written as an async
function, as in the following.
A quick comparison shows that the async
function replaces the asterisk (*
) with async
and the yield
with await
, and that’s it.
The main improvements over Generator are focused on.
- Built-in executors
- better semantics
- Promise return values
As you will see here, Async/Await is essentially the syntactic sugar of Promise: the Async function returns a Promise object.
Let’s take a look at the actual Babel transformed code to make it easier to understand
|
|
It’s easy to see that the call is eventually converted to a Promise based call, but the original three lines of code are converted to 52 lines of code, which in some scenarios imposes a cost.
For example, Vue3 does not use ? (optional chain operator notation) for the following reasons.
Although the use of ? s simplicity, the actual packaging is more redundant, increasing the size of the package and affecting the loading speed of Vue3, which is a sore point with the simplicity of Async/Await syntax.
Ignoring the deeper runtime performance for the moment, is Async/Await perfect, just in terms of how the code is used?
Take the “request N retries” implementation as an example.
|
|
A common implementation idea is to pass the handles of the Resolve and Reject of a Promise into the iteration function to control the internal state transformation of the Promise, but what if you use Async/Await? Obviously not very well, exposing some of its shortcomings.
- Lack of complex control processes, such as always, progress, pause, resume, etc.
- Internal state is not controlled and error catching relies heavily on try/catch
- Lack of interrupt methods and the inability to abort
Of course, some of the requirements may be rare from the EMCA specification’s point of view, but if they were included in the specification, it would reduce the headache for front-end programmers when choosing an asynchronous process control library.
4. Summary
Promise and Async/Await are both excellent solutions for front-end asynchronous processing, but they have some shortcomings. As front-end engineering progresses, there will be better solutions to address these issues, so don’t be disappointed, the future is still looking bright.
From the evolution of Promise and Async/Await, we can actually feel the hard work and whimsy of front-end people in the world of JavaScript, and this kind of thinking and approach can also be sunk into our daily requirements development, so that we can use them in a dialectical way to pursue more extreme solutions.