What are async and await doing
Any name has meaning, and it is understood literally first. async is the abbreviation for "asynchronous", while await means waiting. So it should be well understood that async is used to declare that a function is asynchronous, while await waits for an operation to complete.
So what exactly does async/await do? Let’s briefly introduce it first.
- async/await is a new way to write asynchronous code. The previous asynchronous code scheme was callbacks and promises.
- async/await is based on promise. (Students who are not familiar with promises can check out the correct posture for getting started with this article)
- async/await is like promise, and is also non-blocking.
- async/await makes the asynchronous code look and behave more like synchronous code. This is exactly what it is capable of.
What is async's function
The key to this problem is how the async function handles its return value!
We certainly hope that it can directly return the value we want through the return statement, but if that's the case, it seems like there is nothing wrong with await. So, try writing a piece of code to see what it will return:
<script> async function test(){ return 'hello async'; } let result = test(); (result); </script>
When I saw the output, I suddenly realized it - the output is a Promise object.
Promise {<resolved>: "hello async"}
Therefore, the async function returns a Promise object. The async function (including function statements, function expressions, and Lambda expressions) will return a Promise object. If a direct quantity is returned in the function, async will encapsulate this direct quantity into a Promise object through ().
The async function returns a Promise object, so when the outermost layer cannot use await to get its return value, of course we should use the original method: then() chain to process this Promise object, just like this
async function test(){ return 'hello async'; } test().then((val) => { (val); //hello async })
Now, let’s think about it, what if the async function does not return a value? It's easy to think that it returns (undefined).
Recalling the characteristics of Promise - no wait, so if you execute the async function without await, it will execute immediately, return a Promise object, and will never block the following statement. This is exactly the same as a normal function that returns a Promise object.
Then the next key point is the await keyword.
What are you waiting for
Generally speaking, await is believed to be waiting for an async function to complete. However, according to syntax, what await is waiting for is an expression, and the result of this expression is a Promise object or other value (in other words, there is no special limitation).
Because the async function returns a Promise object, await can be used to wait for the return value of an async function - this can also be said to be await waiting for the async function, but be clear that it is actually a return value. Note that await is not only used to a Promise object, it can await the result of any expression, so await can actually be followed by ordinary function calls or direct quantities. So the following example works completely correctly.
function getSomething(){ return "something"; } async function testAsync(){ return ('hello async'); } async function test(){ let v1 = await getSomething(); let v2 = await testAsync(); (v1,v2); } test(); ('I executed'); //The execution result is://I've executed it//something,hello async
await waited for what to wait, then
await waits for something it wants to wait for, a Promise object, or other value, and then what? I have to say first that await is an operator used to make up expressions, and the result of the operation of the await expression depends on something like that.
If what it waits for is not a Promise object, the await expression's operation result is what it waits for.
If it waits for a Promise object, await will get busy. It will block the subsequent code, wait for the Promise object to resolve, and then get the value of resolve as the result of the await expression.
I feel panicked when I see the word blocking above... Don't worry, this is why await must be used in the async function. The async function call will not cause blockage (that is, the code on line 13 will not be blocked), and all the blockages inside it are encapsulated in a Promise object to be executed asynchronously.
What did async/await do for us
Make a simple comparison
As mentioned above, async will encapsulate the return value of the subsequent function (function expression or Lambda) into a Promise object, and await will wait for the promise to complete and return the result of its resolve.
Now let’s give an example, use setTimeout to simulate time-consuming asynchronous operations. Let’s first see how to write without async/await.
function takeLongTime(){ return new Promise((resolve) => { setTimeout(() => resolve('long time value'),1000); }) } takeLongTime().then((v) => { ('get:',v); })
If you use async/await instead, that would be the case.
function takeLongTime(){ return new Promise((resolve) => { setTimeout(() => resolve('long time value'),1000); }) } async function test(){ let v = await takeLongTime();//Waiting for the result of the asynchronous operation and blocking the execution of subsequent code (v); }
The sharp-eyed classmates have discovered that takeLongTime() has not been declared as async. In fact, takeLongTime() itself is the return Promise object. The results of adding or not adding async are the same. If you don’t understand, please go back and see the above “What role does async play”.
Another question arises. The difference between these two pieces of code processing asynchronous calls (actually the processing of Promise objects) is not obvious. Even using async/await requires more code. So what are its advantages?
The advantage of async/await is to handle the then chain
A single Promise chain cannot find the advantages of async/await, but if you need to deal with the then chain composed of multiple Promises, the advantages can be reflected (it is very interesting. Promise solves the problem of multi-layer callbacks through the then chain, and now it is used to further optimize it).
Suppose a business is completed in multiple steps, each step is asynchronous and depends on the results of the previous step. We still use setTimeout to simulate asynchronous operations:
/* * Pass the parameter n, indicating the execution time of this function (milliseconds) * The result of execution is n+200, this value will be used in the next step */ function takeLongTime(n){ return new Promise((resolve) => { setTimeout(() => resolve(n + 200),n); }) } function step1(n){ (`step1 with ${n}`); return takeLongTime(n); } function step2(n){ (`step2 with ${n}`); return takeLongTime(n); } function step3(n){ (`step3 with ${n}`); return takeLongTime(n); }
Now use the Promise method to implement the processing of these three steps.
function doIt(){ ('doIt'); let time1 = 300; step1(time1) .then((time2) => step2(time2)) .then((time3) => step3(time3)) .then((result) => { (`result is ${result}`); ("doIt"); }) } doIt(); //The execution result is://step1 with 300 //step2 with 500 //step3 with 700 //result is 900 //doIt: 1510.2490234375ms
The output result is a parameter of step3() 700 + 200 = 900. The doIt() sequence performed three steps, and a total of 300 + 500 + 700 = 1500 milliseconds are used, which is consistent with the results of the ()/() calculation.
If it is implemented using async/await, this will be the case.
async function doIt() { ('doIt'); let time1 = 300; let time2 = await step1(time1);// Assign the value of the Promise object resolve(n+200) to time2 let time3 = await step1(time2); let result = await step1(time3); (`result is ${result}`); ('doIt'); } doIt(); //The execution result is://step1 with 300 //step2 with 500 //step3 with 700 //result is 900 //doIt: 1512.904296875ms
The result is the same as the previous Promise implementation, but does this code look much clearer, almost the same as the synchronous code.
There are even cooler ones
Now, changing the business requirements is still three steps, but each step requires the results of each step before.
/* * Pass the parameter n, indicating the execution time of this function (milliseconds) * The result of execution is n+200, this value will be used in the next step */ function takeLongTime(n){ return new Promise((resolve) => { setTimeout(() => resolve(n + 200),n); }) } function step1(n){ (`step1 with ${n}`); return takeLongTime(n); } function step2(m,n){ (`step2 with ${m} + ${n}`); return takeLongTime(m + n); } function step3(k,m,n){ (`step3 with ${k} + ${m} + ${n}`); return takeLongTime(k + m + n); }
This time I first use async/await to write:
async function doIt() { ('doIt'); let time1 = 300; let time2 = await step1(time1);// Assign the value of the Promise object resolve(n+200) to time2 let time3 = await step2(time2,time1); let result = await step3(time3,time2,time1); (`result is ${result}`); ('doIt'); } doIt(); //The execution result is://step1 with 300 //step2 with 500 + 300 //step3 with 1000 + 500 + 300 //result is 2000 //doIt: 2916.655029296875ms
Except for the longer execution time, it seems that there is no difference from the previous example! Don't worry, think carefully about what would it look like if I wrote it as a Promise method?
function doIt() { ('doIt'); let time1 = 300; step1(time1) .then((time2) => { return step2(time1,time2) .then((time3) => [time1,time2,time3])//Step3 needs to use time1, time2, time3, so it needs to return }) .then((times) => { let [time1,time2,time3] = times; return step3(time1,time2,time3) }) .then((result) => { (`result is ${result}`); ('doIt'); }) } doIt(); //The execution result is://step1 with 300 //step2 with 300 + 500 //step3 with 300 + 500 + 1000 //result is 2000 //doIt: 2919.49609375ms
Does it look a bit complicated? Those bunch of parameter processing are the fatal points of the Promise solution - parameter transmission is too troublesome, and you can feel dizzy when you look at it!
Note
As of now, you already understand async/await? But in fact, there are some things that have not been mentioned - Promise may reject, how to deal with it?
The Promise object behind the await command may be rejected, so it is best to put the await command in the try...catch code block.
async function myFunction() { try { await somethingThatReturnAPromise(); } catch (err){ (err); } } //Another way of writingasync function myFunction() { await somethingThatReturnAPromise().catch(function(err) { (err); }) }