With the release of Node 7, more and more people are exploring async/await, which is said to be the ultimate solution for asynchronous programming. The first time I saw this set of keywords was not in JavaScript, but in c# 5.0 syntax. C# async/await needs to be used in.net Framework 4.5 and above, so I was sad for a while that in order to be compatible with XP, we couldn’t develop software that used.net Framework 4.0 and above.

I talked about this earlier in “Flatten” Chat Asynchronous Calls. Async /await are great features in both C# and JavaScript, and they are very sweet syntactic sugar. C#’s async/await implementation relies on the Task or Task

class, while JavaScript’s async/await implementation relies on promises.

Now leave C# and the.net Framework behind and focus on JavaScript async/await.

What are async and await doing

Any name makes sense, so let’s take it literally. Async is short for ‘asynchronous’ and await can be thought of as short for async wait. So it makes sense that async is used to declare that a function is asynchronous and await is used to wait for an asynchronous method to complete.

There is also an interesting syntax that says await can only appear in async functions. Then careful friends will have a question, if await can only appear in async function, how should the async function be called?

If you want to call an async function with await, you must wrap an async function around the call and then… Into an endless cycle of never coming out…

If async functions do not need to be called with await, then what is async really for?

What does async do

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

We would certainly like it to return the desired value directly via the return statement, but if so, there seems to be no await. So write some code to see what it returns:

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

const result = testAsync();
console.log(result);Copy the code

When I saw the output, I realized it was a Promise object.

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

So the async function returns a Promise object. You can get this information from the documentation as well. Async functions (including function statements, function expressions, and Lambda expressions) return a Promise object. If a function returns an immediate Promise, async wraps the immediate Promise object with promise.resolve ().

The async function returns a Promise object, so in cases where the outermost layer cannot await the return value, we should of course process the Promise object the way it is: then() chains, like this

testAsync().then(v => { console.log(v); // output hello async});Copy the code

Now, what if async does not return a value? It’s easy to imagine that it returns promise.resolve.

Think of Promise as a feature-no wait, so an async function executed without await will execute immediately, return a Promise object, and never block subsequent statements. This is no different than a normal function that returns a Promise object.

So the next key point is the await keyword.

Await what exactly is waiting for

“Await” is generally considered to be waiting for an async function to complete. But syntactically, await is an expression that evaluates to a Promise object or some other value (in other words, without special qualification).

Since an async function returns a Promise object, await can be used to wait for the return value of an async function — it can also be said to await an async function, but be clear that it is waiting for a return value. Note that await is not just used to wait for promises, it can wait for the result of any expression, so await is actually followed by ordinary function calls or direct quantities. 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();Copy the code

Await await, and then

Await waits for what it is waiting for, a Promise object, or some other value, and then what? I have to say first that await is an operator used to form an expression and the result of an await expression depends on what it is waiting for.

If it waits for something other than a Promise object, the result of the await expression operation is what it waits for.

If it waits for a Promise object, await is busy and blocks the following code, waiting for the Promise object resolve, and then getting the value of resolve as the result of the await expression.

See the word “block” above, panic… Rest assured, this is why await must be used in async functions. Async function calls do not block, and all blocking within them is wrapped up in a Promise object and executed asynchronously.

What async/await is doing for us

A simple comparison

As explained above async encapsulates 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 resolve.

Now, for example, using setTimeout to simulate a time-consuming asynchronous operation, let’s see what happens without async/await

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

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

What if we use async/await instead

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

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

test();Copy the code

Eagle-eyed students have noticed that takeLongTime() is not declared async. In fact, takeLongTime() itself is the returned Promise object, and async doesn’t matter. If you’re not sure what async does, go back and see what async does.

Another question arises, there is no obvious difference between the two codes in the processing of asynchronous calls (actually the processing of Promise objects), and even more code needs to be written to use async/await, so what are its advantages?

The advantage of async/await is processing then chains

The advantages of async/await are not found in a single Promise chain, but in cases where you need to deal with then chains consisting of multiple promises (interestingly, promises use then chains to solve the problem of multi-layer callbacks, Now optimize it further with async/await).

Suppose a business is done in multiple steps, each of which is asynchronous and depends on the results of the previous step. We still use setTimeout to simulate asynchronous operations:

/** * pass in n to indicate the execution time of this function (milliseconds) * the result of execution is 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); }Copy the code

Now use the Promise approach to implement all three steps of processing

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 msCopy the code

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

If you implement async/await, it will 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();Copy the code

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

There’s something even cooler

Now change the business requirements to the same three steps, but each step requires the result of each 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);
}Copy the code

I’ll write async/await:

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.387msCopy the code

There seems to be no difference from the previous example, except that the execution time is longer. Don’t worry, what would it look like 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();Copy the code

Do you feel a little complicated? That bunch of parameter processing is the death of the Promise scheme – parameter passing is too troublesome to watch dizzy!

Wash your sleep

So far, you understand async/await? But there’s something left unsaid — Promise, possible reject, what do you do? What if you need to process three steps in parallel and wait for all the results?

Ruan Yifeng teacher has said, I am too lazy to say.