ECMAScript 6 笔记(23)- async 函数

原创 乘风逐月 随笔 ES6 75阅读 13 天前 举报

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

一、语法

1.async 函数的多种使用形式
2.返回值

async 函数返回一个 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误。也就是说,只有 async 函数的异步操作执行完,才会执行 then 方法指定的回调函数。
例如:

上例中,函数 getTitle 内部有三个操作:抓取网页、取出文本、匹配标题,只有这三个操作全部完成,才会执行 then 方法里面的 console.log 。

3.await 命令

正常情况下,await 命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

另一种情况是, await 命令后面是一个定义 then 方法的对象,那么 await 会将其等同于 Promise 对象。
await 命令后面的 Promise 对象如果变成为 reject 状态,则 reject 的参数会被 catch 方法的回调函数接收到。

上例中,await 语句前面没有 return ,但是 reject 方法的参数依然传入了 catch 方法的回调函数。这里如果在 await 前面加上 return,效果时一样的。

任何一个 await 语句后面的 Promise 对象变为 reject 状态,那么整个 async 函数都会中断执行。例如:

如果希望一个异步操作失败,也不会中断后面的异步操作。这时可以将第一个 await 放在 try...catch 结构里面,这样不管这个异步操作是否成功,第二个 await 都会执行。

另一种方法是 await 后面的 Promise 对象再跟一个 catch 方法,处理前面可能出现的错误。

4.错误处理

如果 await 后面的异步操作出错,那么等同于 async 函数返回的 Promise 对象被 reject。防止出错的方法,也是将其放在 try...catch 代码块之中。如果有多个 await 命令,可以统一放在 try...catch 结构中。

5.使用注意点

(1)await 命令后面的 Promise 对象,运行结果可能是 reject,所以最好把 await 命令放在 try...catch 代码块中。
(2)多个 await 命令后面的异步操作,如果不存在继发关系,最好让他们同时出发。

(3)await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
(4) async 函数可以保留运行堆栈

二、async 函数与 Generator 函数

比较发现,async 函数与 Generator 函数很相似,async 函数就是将 Generator 函数 星号替换成 async,将 yield 替换成 await。
async 函数对 Generator 函数的改进,体现在一下四点:
(1)内置执行器
Generator 函数的执行必须靠执行器,而 async 函数自带执行器。即 async 函数的执行,与普通函数的执行一模一样,只要调用函数就可以自动执行,输出最后的结果。不想 Generator 函数,需要调用 next 方法,或者 co 模块,才能真正执行得到最后的结果。
(2)更好的语义
async 和 await,比起星号和 yield,语义更清楚。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性
co 模块约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以是 Promise 对象和原始类型的值(数值、字符串、布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
(4)返回值是 Promise
async 函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便,可以用 then 方法指定下一步的操作。
进一步说,async 函数完全可以看做多个异步操作,包装成的一个 Promise 对象,而 await 命令就是内部 then 命令的语法糖。

三、异步遍历器

1.异步遍历器

Iterator 接口是一种数据遍历的协议,只要调用遍历器对象的 next 方法,就会得到一个对象,表示当前遍历指针所在的那个位置的信息。next 方法返回的对象的结构是 {value, done},其中 value 表示当前的数据的值,done 是一个布尔值,表示遍历是否结束。

这里隐含规定,next 方法必须是同步的,只要调用就必须立刻返回值。即一旦执行 next方法,就必须同步地得到 value 和 done 这两个属性。如果遍历器指针正好指向同步操作没有问题,但对于异步操作就不太合适。目前的解决方法是,Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即 value 属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而done 属性则还是同步产生的。

ES2018 引入 “异步遍历器”,为异步操作提供原生的遍历器接口,即 value 和 done 这两个属性都是异步产生。

2.异步遍历的接口

异步遍历器的最大语法特点,就是调用遍历器的 next 方法,返回的是一个 Promise 对象。

上例中,asyncIterator 是一个异步遍历器,调用 next 方法以后,返回一个 Promise 对象。因此,可以调用 then 方法指定,这个 Promise 对象的状态变为 resolve 以后的回调函数。回调函数的参数,则是一个具有 value 和 done 两个属性的对象,这个跟同步遍历器是一样的。

一个对象的同步遍历器的接口,部署在 Symbol.iterator 属性上面。同样的,对象的异步遍历器接口,部署在 Symbol.asyncIterator 属性上面。任何对象,只要它的 Symbol.asyncIterator 属性有值,就表示应该对它进行异步遍历。

四、for await...of 器

for...of 循环用于遍历同步的 Iterator 接口。for await...of 循环,则是用于遍历异步的 Iterator 接口。

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复