The 2020-06-04 update

Async /await in JavaScript is the keyword in the asyncFunction feature. To date, browsers and Node (v7.6+) support this feature in addition to IE. Detailed support can be found here.


The first time I saw the async/await keyword was not in the JavaScript language, but in the C# 5.0 syntax. C# async/await needs to be used on.NET Framework 4.5 and above, so I was sad for a while – in order to be compatible with XP, we can’t develop software that uses.NET Framework above 4.0.

I talked about this earlier in my article “Smash Asynchronous Call” Flattening “. Async /await is a nice feature in both C# and JavaScript, and they are also very sweet syntactic sugar. C#’s async/await implementation can’t do without the Task or Task\

class, and JavaScript’s async/await implementation can’t do without the Promise.
\>

Now forget about C# and the.NET Framework and concentrate on JavaScript async/await.

1. What are async and await doing

Any name is meaningful, so take it literally. Async is short for ‘asynchronous’ and await can be considered short for’ async wait ‘. So it should be easy to understand that async is used to declare a function to be asynchronous, and await is used to wait for an asynchronous method to complete.

There is also an interesting syntax that states that await can only occur in the async function. Then the attentive friend will ask, if await can only appear in the async function, how should the async function be called?

If we need to call an async function via await, then we must wrap another async function outside of the call and then… Into an endless cycle, never to return…

If async function does not need await to be called, what is async really for?

1.1. What is the role of async

The key to this problem is how the async function handles its return value!

We would certainly like it to return the value we want directly through the return statement, but if it does, there seems to be no await. So, write some code to try it out and see exactly what it returns:

async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);

When you see the output, it’s a Promise object.

c:\var\test> node --harmony_async_await .
Promise { 'hello async' }

So, the async function returns a Promise object. This information is also available from the documentation. The async function (which includes function statements, function expressions, and Lambda expressions) returns a Promise object. If you return a direct value in a function, async wraps it in a Promise object via promise.resolve ().

[2020-06-04]

Promise. Resolve (x) can be thought of as shorthand for new Promise(resolve => resolve(x)), and can be used to quickly wrap literal objects or other objects into Promise instances.

The async function returns a Promise object, so in the case where the outermost layer cannot retrieve its return value with await, we should of course handle the Promise object the way we did before: the then() chain, like this

testAsync().then(v => { console.log(v); // Print the "async" async});

Now let’s go back and think about what happens if the async function returns no value. It’s easy to imagine that it will return promise.resolve (undefined).

Consider the Promise feature — there is no wait, so if you execute the async function without await, it executes immediately, returns a Promise object, and never blocks subsequent statements. This is no different than a normal function that returns a Promise object.

So the next key point is the await keyword.

1.2I am awaiting what on earth

In general, we think of await as waiting for an async function to complete. Syntactically, however, await is waiting for an expression that evaluates to a Promise object or some other value (in other words, no special qualifier).

Since async returns a Promise object, await can be used to wait for the return value of an async function — this could also be saying await for async, but be aware that what it is waiting for is actually a return value. Note that await does not just wait on a Promise object. It can wait on the result of any expression. So await can actually be followed by a normal function call or a direct quantity. So the following example works perfectly

function getSomething() {
    return "something";
}

async function testAsync() {
    return Promise.resolve("hello async");
}

async function test() {
    const v1 = await getSomething();
    const v2 = await testAsync();
    console.log(v1, v2);
}

test();

We are awaiting the await

“Await” awaits what it is waiting for, a Promise object or some other value, and then what? I have to start by saying that “await” is an operator that is used to compose an expression and the result of an expression “await” depends on something that it is waiting on.

If it is waiting for something other than a Promise, then the result of the await expression is what it is waiting for.

If it waits for a Promise object, await will get busy, it will block the following code, wait for the Promise object to resolve, and then get the value of resolve as the result of the await expression.

See the word blocking above, flustering… Relax, this is why await must be used in the async function. The async function call does not cause a block; all blocks inside it are executed asynchronously, wrapped in a Promise object.

2. What are we doing async/await

2.1. Make a simple comparison

We’ve already shown that async wraps the return value of a subsequent function (function expression or Lambda) into a Promise object, and await waits for the Promise to complete and returns the result of its resolve.

For example, using setTimeout to simulate a time-consuming asynchronous operation, let’s see what would happen without async/await

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

takeLongTime().then(v => {
    console.log("got", v);
});

If I use async/await instead, it will look like this

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

async function test() {
    const v = await takeLongTime();
    console.log(v);
}

test();

Those of you eagle-eyed have noticed that TakeLongTime () is not declared async. In fact, TakeLongTime () itself is the Promise object returned, with or without async, so refer back to “async for what it does” above.

Another question arises. There is no obvious difference between the two ways of handling asynchronous calls (which is actually handling Promise objects). Even using async/await requires a bit more code to be written.

2.2. The advantage of async/await is the processing of the then chain

A single Promise chain does not reveal the advantage of async/await, but it does when processing a Then chain consisting of multiple Promises. (Interestingly, Promises use a Then chain to handle multiple callbacks. Now optimize it further with async/await).

Consider a business that is completed in multiple steps, each of which is asynchronous and dependent on the outcome of the previous step. We still use setTimeout to simulate asynchronous operations:

/** * = n + 200; /** * = n + 200; */ Function TakeLongTime (n) {return new Promise(resolve => {setTimeout(() => resolve(n + 200), n); }); } function step1(n) { console.log(`step1 with ${n}`); return takeLongTime(n); } function step2(n) { console.log(`step2 with ${n}`); return takeLongTime(n); } function step3(n) { console.log(`step3 with ${n}`); return takeLongTime(n); }

Now use Promise to do all three steps

function doIt() { console.time("doIt"); const time1 = 300; step1(time1) .then(time2 => step2(time2)) .then(time3 => step3(time3)) .then(result => { console.log(`result is ${result}`); console.timeEnd("doIt"); }); } doIt(); // c:\var\test>node --harmony_async_await . // step1 with 300 // step2 with 500 // step3 with 700 // result is 900 // DoIt: 1507.251 ms

Result is the parameter of step3() 700 + 200 = 900. DoIt () executes the three steps in sequence, taking 300 + 500 + 700 = 1500 milliseconds, as calculated by console.time()/ console.timeend ().

What if I were to implement async/await, it would look like this

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

The result is the same as the previous Promise implementation, but doesn’t the code look much cleaner, almost like the synchronization code

There are even cooler ones

Now change the business requirements to the same three steps, but each step needs the result of the previous step.

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(m, n) {
    console.log(`step2 with ${m} and ${n}`);
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    console.log(`step3 with ${k}, ${m} and ${n}`);
    return takeLongTime(k + m + n);
}

This time write async/await first:

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 800 = 300 + 500
// step3 with 1800 = 300 + 500 + 1000
// result is 2000
// doIt: 2907.387ms

It seems to be the same as the previous example, except that the execution time is longer. Don’t worry, really think about what would happen if you implemented it as a Promise?

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

Does it seem a little complicated? That pile of argument processing was the dead end of the Promise scheme — passing arguments was just too cumbersome to watch!

3. Go to bed

So far, have you understood async/await? But there’s something else I didn’t mention — a Promise is a reject, what do you do with it? What if you need to do three steps in parallel and wait for all the results?

Ruan Yifeng has already said that, I will not bother to say.


4. Recommend relevant articles

  • Use async/await in WeChat applet 2020-03-10
  • Code review, analysis of common problems with asynchronous calls 2020-04-12
  • Proxy encapsulates the asynchronous call of WeChat applet The 2020-04-9
  • Improved asynchronous encapsulation: handles asynchronous calls with return values 2020-04-25
  • From hell to heaven, Node callbacks to async/await transition 2017-01-02
  • Asynchronous programming requires “awareness” 2018-02-23
  • The async/await syntax of a try-catch implementation is never used for error handling 2017-10-31

5. Come and learn from the border town

Update the @ 2020-11-14

TypeScript from Beginnings to Practice (2021 Edition)

6. About reprintingAdd @ 2020-03-05

Readers often ask whether they can reprint.

I welcome you to reprint, but the reprint must indicate the author and source, thank you!




Please pay attention to the public accountBorder town inn

After reading, don’t go first, point a praise ⇓, praise a line!