重新认识 async/await

368 阅读3分钟

这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

背景

最近刚上手用 TypeScript,然后时不时会见到“奇奇怪怪的报错”。比如说今天这个:

在这里插入图片描述

这是啥子情况?我返回的不是个对象吗,promise 是怎么回事。难道因为是 async?瞎蒙着给 getCategoryTree 调用的地方加了 await,就不会报错了!果然是 async 搞的鬼。

在这里插入图片描述

emmm.. 看来对 async/await 的认知还不到位,得再认识认识 ——


async 函数

async 函数的返回值

async函数一定会返回一个promise对象。如果 async 函数显示指定的返回值不是一个 promise ,那么这个返回值将会被隐式地包装在一个 promise 中被 resolve;对于异常或错误的情况,则会通过async函数中抛出 reject

resolve 的情况:

async function foo() {
   return 1
}
// 等价于:
function foo() {
   return Promise.resolve(1)
}

// Promise {<fulfilled>: 'hello'}

reject 的情况:

async function bar(){
    throw new Error('sth error');
}
// 等价
async function bar(){
    return Promise.reject(new Error('sth error'))
}

// Promise {<rejected>: Error: sth error

await

async 函数可能包含0个或者多个 await 表达式。如果 async 函数内没有 await 表达式,则函数体都是同步运行的。如果有多个 awaitasync 会等待所有 awaitpromise 执行完毕才会发生状态改变。

await 表达式会中断函数体的执行,直到等待的 promise 异步操作完成后才恢复进程。

await 关键字只在 async 函数内有效。

async/await 的实战场景

  1. B 请求依赖于 A 请求的返回结果(异步、依次执行)
const d1 = async ()=>{
    const res_a = await requestA()
    const res_b = await requestB(res_a)
    console.log(resB)
}
  1. C 请求依赖于 A、B 请求的返回(A、B同步执行,C随后执行)
const requestA = async ()=>{}
const requestB = async ()=>{}
const d2 = async ()=>{
    const [res_a, res_b] = await Promise.all([ requestA(), requestB() ])
    const res_c = await requestC(res_a, res_b)
    console.log(res_c)
}

Generator 函数

async/await 和 Generator 的渊源

async 函数就是 Generator 函数的语法糖

asyncGenerator 函数的改进:

  • 内置执行器,调用函数后自动执行,不需要通过 .next() 进行下一步操作。
  • 返回 Promise。从上文可知,async 函数最终会返回一个 Pormise 对象,这使我们可以方便的用 .then() 进行链式操作,也可以继续作为 await 的参数进行同步操作。

Generator 函数基本特性和使用

  • 定义 Generator 函数只需要比普通函数多个 * 号;
  • 调用一个生成器函数不会马上执行里面的语句,而是返回一个迭代器对象。
  • 直到手动调用迭代器的 next 方法,函数体内的语句才会开始执行,并到第一个出现的 yield 位置为止。
  • yield 关键字向外部返回一个值,这个值在 next 方法返回对象中拿到。
  • yield 关键字不会结束函数的执行,只是暂停执行,直到外部下一次调用 next 方法,生成器函数继续执行。
  • 在调用生成器的 next 方法时可以传递一个参数,所传入的参数将会作为 yield 语句的返回值
  • 另外,生成器函数的 throw 方法可以抛出一个异常。
function * gen(){
    console.log('--- start ---');
    const res = yield 'foo'
    console.log(res) // bar 
}

const iterator = gen()       //此时并不会马上执行函数体内的语句,而是返回一个迭代器对象
const res = iterator.next()  //函数开始执行,直至碰到第一个 yield 关键字,暂停。
console.log(res)             // {value:'foo', done:false}。next 方法返回一个对象 `{value, done}` 。value 指 yield 关键字后跟着的返回值(此处为 'foo'),done 表示是否生成器是否已全部执行完毕。
iterator.next('bar')         //next 传递的参数,将作为 yield 语句的返回值

参考链接

async 函数的含义和用法(阮一峰)
async 函数(MDN)
7张图,20分钟就能搞定的async/await原理!(林三心)
Generator 函数(MDN)