说明

一句话解释:async 函数,就是 Generator 函数的语法糖。

  • 建立在 promise 之上。所以,不能把它和回调函数搭配使用。但它会声明一个异步函数,并隐式地返回一个Promise。因此可以直接return变量,无需使用 Promise.resolve 进行转换。
  • 和 promise 一样,是非阻塞的。但不用写 then 及其回调函数,这减少代码行数,也避免了代码嵌套。而且,所有异步调用,可以写在同一个代码块中,无需定义多余的中间变量。
  • 它的最大价值在于,可以使异步代码,在形式上,更接近于同步代码。
  • 它总是与 await 一起使用的。并且,await 只能在 async 函数体内
  • await 是个运算符,用于组成表达式,它会阻塞后面的代码。如果等到的是 Promise 对象,则得到其 resolve 值。否则,会得到一个表达式的运算结果。

开始

一个简单的例子

JavaScript 代码:

  1. const doSomethingAsync = () => {
  2. return new Promise((resolve) => {
  3. setTimeout(() => resolve('I did something'), 3000)
  4. })
  5. }
  6. const doSomething = async () => {
  7. console.log(await doSomethingAsync())
  8. }
  9. console.log('Before')
  10. doSomething()
  11. console.log('After')

async/await总结 - 图1

一切都是 promise

async关键字添加到任何函数,意味着该函数将返回一个Promise。

  1. const aFunction = async () => {
  2. return 'test'
  3. }
  4. aFunction().then(alert) // This will alert 'test'

并且与下面的代码等价:

  1. const aFunction = async () => {
  2. return Promise.resolve('test')
  3. }
  4. aFunction().then(alert) // This will alert 'test'

代码更易于阅读 (重点看这个Demo)

每一个步骤都需要之前每个步骤的结果。

  1. const getFirstUserData = () => {
  2. return fetch('/users.json') // get users list
  3. .then(response => response.json()) // parse JSON
  4. .then(users => users[0]) // pick first user
  5. .then(user => fetch(`/users/${user.name}`)) // get user data
  6. .then(userResponse => response.json()) // parse JSON
  7. }
  8. getFirstUserData()

并且与下面的代码等价:

  1. const getFirstUserData = async () => {
  2. const response = await fetch('/users.json') // get users list
  3. const users = await response.json() // parse JSON
  4. const user = users[0] // pick first user
  5. const userResponse = await fetch(`/users/${user.name}`) // get user data
  6. const userData = await user.json() // parse JSON
  7. return userData
  8. }
  9. getFirstUserData()

链接多个 async(异步) 函数

每一个步骤都需要之前每个步骤的结果。

  1. const promiseToDoSomething = () => {
  2. return new Promise(resolve => {
  3. setTimeout(() => resolve('I did something'), 10000)
  4. })
  5. }
  6. const watchOverSomeoneDoingSomething = async () => {
  7. const something = await promiseToDoSomething()
  8. return something + ' and I watched'
  9. }
  10. const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
  11. const something = await watchOverSomeoneDoingSomething()
  12. return something + ' and I watched as well'
  13. }
  14. watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
  15. console.log(res)
  16. })
  17. //I did something and I watched and I watched as well

2020更好的错误处理

  1. // 这里使用了一个工具函数 handle,如此就可以避免 Unhandled promise rejection 报错,还能细粒度的处理错误。
  2. const handle = (promise) => {
  3. return promise
  4. .then(data => ([data, undefined]))
  5. .catch(error => Promise.resolve([undefined, error]));
  6. }
  7. async function userProfile() {
  8. let [user, userErr] = await handle(getUser());
  9. if(userErr) throw new Error('Could not fetch user details');
  10. let [friendsOfUser, friendErr] = await handle(
  11. getFriendsOfUser(userId)
  12. );
  13. if(friendErr) throw new Error('Could not fetch user\'s friends');
  14. let [posts, postErr] = await handle(getUsersPosts(userId));
  15. if(postErr) throw new Error('Could not fetch user\'s posts');
  16. showUserProfilePage(user, friendsOfUser, posts);
  17. }

async/await中的错误处理

async常用异常处理

  1. async function error() {
  2. try{
  3. //do something
  4. }catch(err){
  5. console.log(err)
  6. }
  7. }

async中的错误处理

因为async的返回值也是个promise,跟promise的错误处理差不多。此外,async里面throw Error 相当于返回Promise.reject

  1. async function F(){
  2. throw new Error('test1')
  3. }
  4. var f=F();
  5. f.catch(function(e){console.log(e)});
  6. // Error:test1

await中的错误处理

在async中,await的错误相当于Promise.reject。

  1. async function F(){
  2. await Promise.reject('Error test1').catch(function(e){
  3. console.log(e)
  4. })
  5. }
  6. var f=F(); // Error:test1

await中的promise.reject必须要被捕获

await如果返回的是reject状态的promise,如果不被捕获,就会中断async函数的执行。

  1. async function F(){
  2. await Promise.reject('Error test1');
  3. await 2
  4. }
  5. var f=F()

上述代码中,前面的Promise.reject没有被捕获,所以不会执行await 2

支持

async/await总结 - 图2

常见问题

参考

  1. 总结一下ES6/ES7中promise、generator和async/await中的异常捕获方法
  2. 用 async 和 await 编写现代 JavaScript 异步代码 – JavaScript 完全手册(2018版)
  3. 细说 async/await 相较于 Promise 的优势

总结

每一个特性的出现,总有它的用途,而只有用了,才能更好地理解它。

JavaScript的异步编写方式,从 回调函数 到 Promise、Generator 再到 Async/Await。表面上只是写法的变化,但本质上则是语言层的一次次抽象。让我们可以用更简单的方式实现同样的功能,而不需要去考虑代码是如何执行的。

换句话说就是:异步编程的最高境界,就是根本不用关心它是不是异步。