The paper contains 4898 words and is expected to last 10 minutes

Photo credit: https://unsplash.com/@grohsfabian

Async/await has been introduced in NodeJS 7.6 and currently runs on almost all advanced browsers. This is definitely the best JavaScript add-on syntax since 2017, not one.

Async/Await 101

• Async/await is a new way to write asynchronous code. Writing asynchronous code used to use Callbacks and Promises.

• Async/await is really just a sugar-coated syntax based on Promises and cannot be used with base callbacks or node callbacks.

• Async/await like Promises are non-blocking.

• Async/await gives asynchronous code a more synchronous code style, which is an advantage.

syntactic

Suppose the function getJSON returns a promise that is resolved through some JSON object. We just want to call it, log the JSON, and return “done.”

Here’s how to do these steps with Promises:

const makeRequest = () =>
  getJSON()
    .then(data => {
      console.log(data)
      return "done"
    })

makeRequest()Copy the code

Here is the effect of async/await:

const makeRequest = async () => {
  console.log(await getJSON())
  return "done"
}

makeRequest()Copy the code

There are some differences:

1. Async keyword in front of function. The keyword await can only be used within functions defined by async. Any async function can implicitly return a promise, whose parse value is any value returned from the function (in this case, the string “done”).

2. The above point means that you cannot use await at the top of the code because it is outside the scope of async defined functions.

// this will not work in top level
// await makeRequest()

// this will work
makeRequest().then((result) => {
  // do something
})
import pandas as pdCopy the code

3. await getJSON() means that the console.log call will wait until the getJSON() promise is parsed before it outputs the parsed value.

Why async/await is superior?

Credit: https://unsplash.com/@amartino20

1. Keep it clean

See how many lines of code you can save by using async/await! Even in the contrived example above, you can clearly see that quite a few lines of code have been saved. It also eliminates the need to create an anonymous function to handle the response, or provide name data for variables that are not needed, and avoids nested code. These small advantages quickly add up and become more obvious in the following code examples.

2. Error handling

Async/Wait handles synchronous and asynchronous errors using the same structure and the nice classic try/catch. In the following example with Promises, if JSON parsing fails, try/catch is not processed because it runs in promises. Hence the need to call catch in the Promise and copy the error-handling code, which will (hopefully) be more sophisticated than console.log for production-ready code.

const makeRequest = () => {
  try {
    getJSON()
      .then(result => {
        // this parse may fail
        const data = JSON.parse(result)
        console.log(data)
      })
      // uncomment this block to handle asynchronous errors
      // .catch((err) => {
      //   console.log(err)
      // })
  } catch (err) {
    console.log(err)  
 }
}Copy the code

Now look at the same code using async/await. The catch block can then handle parsing errors.

const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}Copy the code

3. Qualifications

Imagine that the following code can take some data and, based on some values in the data, decide whether to return the data or continue to get more details.

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}Copy the code

Just looking at the code was a pain in the neck, and it was easy to get lost in the various nesting (level 6), curly braces, and return statements that were only used to pass the final result to the main promise.

But when the example is rewritten using async/ await, the readability is much improved.

const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data
      }
}Copy the code

4. The median

Call Promise E1, then call Promise e2 with the result it returns. Then call Promise e3 with the results of promises. The resulting code is likely to look like this:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      // do something
      return promise2(value1)
        .then(value2 => {
          // do something
                    return promise3(value1, value2)
        })
    })
}Copy the code

If the promise e3 does not need a value of 1, the promise nesting can easily be reduced. If this can’t be tolerated, you can avoid deeper nesting by including both values 1 and 2 in a promise, like this:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      // do something
      return Promise.all([value1, promise2(value1)])
    })
    .then(([value1, value2]) => {
      // do something
                return promise3(value1, value2)
    })
}Copy the code

This approach sacrifices semantic expression for code readability. There is no reason for values 1 and 2 to belong to the same array except to avoid nesting promises.

But with async/ await, the same logic becomes surprisingly simple and intuitive. Think of how much time you spent trying to make Promises easier, when you could have done so much more!

const makeRequest = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)}Copy the code

5. Error stack

Imagine a piece of code that calls multiple Promises in a chain and then makes an error somewhere in the chain.

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then
 (index.js:8:13)
  })Copy the code

The error stack returned from the Promise chain does not show where the error occurred. Worse, it is misleading. The only function name it contains is a callAPromise that doesn’t cause an error at all (but the file and line number are still useful).

But the error stack in async/ await can indicate the function that contains the error

const makeRequest = async () => {
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  throw new Error("oops");
}

makeRequest()
  .catch(err => { 
   console.log(err);
    // output
    // Error: oops at makeRequest (index.js:7:9)
  })Copy the code

This is not a big plus if you develop in a local environment and open files in an editor. But it can be very useful in understanding error logs from production servers. In this case, knowing that the error occurred in the makeRequest is better than knowing that the error came from the next place and the next place and the next place…

6. Debugging

One great advantage of using async/ await is that it is easier to debug. Trying to make Promises has always been a pain, for two reasons.

(1) You cannot set breakpoints in arrow functions that return expressions (without a trunk).

Try setting breakpoints anywhere

(2) If you set a breakpoint in a. Then block and use a debugging shortcut such as step-over, the debugger will not move to the following. Then block because it can only “step over” the synchronized code.

With async/ await, however, there is no need for too many arrow functions, and you can call directly across await just like a normal synchronous call.

7. You can await everything

Finally, await can be used with synchronous and asynchronous expressions. For example, await5 is equivalent to promise.resolve (5). This may not seem useful at first, but when writing a library or a utility function, it’s a big advantage if you don’t know whether the input is synchronous or asynchronous.

If you want to keep track of the time it takes to make some API calls in your application and create a generic function for it, here are the effects of Using Promises:

All API calls return Promises, but what happens if you use the same function to record the time taken by the synchronization function? This usually goes wrong because the synchronization function does not return a promise. To avoid this, makeRequest() is usually written to promise.resolve ().

But if you use async/await, you don’t have to worry about these cases because await can safely handle any value, no matter promises or not.

const recordTime = async (makeRequest) => {
  const timeStart = Date.now();
  await makeRequest(); // works for any sync or async function
  const timeEnd = Date.now();
  console.log('time take:', timeEnd - timeStart);
}Copy the code

conclusion

Async /await is one of the most revolutionary additions to JavaScript in the last few years. It highlights the grammatical confusion of Promises and offers easy-to-understand alternatives.

Admittedly, some argue that using async/await makes asynchronous code look less obvious. But the asynchronous code is recognizable every time you see a call or.then block. It may take a few weeks for people to get used to this new signal, but C# has had this feature for years, and those familiar with it will understand that this minor, temporary inconvenience is nothing more than a flaw.

Leave a comment like follow

We share the dry goods of AI learning and development

Welcome to “core reading” of AI vertical we-media of the whole platform



(Add wechat: DXSXBB, join readers’ circle and discuss the freshest artificial intelligence technology.)