异步是JavaScript的一大难点之一,因此js的异步解决方案也是多种多样的。从最早的回调函数,到Promise对象,到Generator函数,再到现在的async/await。很多人都认为async/await是异步操作的终极解决方案,今天就来简单介绍下它。
async/await 这名字取得就很语义化,async声明一个异步function,await用于等待异步function执行完成。并且语法规定,await只能出现在async函数中。
aysnc
async到底做了什么?
我们看一下下面的代码
我们来看看打印出了什么
没错,它返回了一个Promise对象。从文档中我们知道await用于等待一个Promise对象。
但是在上面的例子中直接返回了一个字符串,它不是Promise对象,async函数会通过Promise.resolve()把它封装成一个Promise对象。
我们回顾一下Promise的特点——无等待。如果这个async函数没有await的话,它会立即执行返回一个Promise对象,不会阻塞后面代码的运行。
await
我们知道await是在等待一个async函数完成,而async函数会返回一个Promise对象,因此await表达式的运算结果就是这个Promise对象resolve()的值。
继续看个例子:
上述代码首先打出了”同步代码”,随后又立即打出”test”,并在一秒后打出了”999”。
我们可以得出以下结论:
- await会阻塞它所在的异步函数的后面代码的执行(因为它需要等到testAsync函数的结果才会往下执行),但是不会阻塞异步函数之外的代码的执行(因为先打印的”同步代码”),其实它内部的阻塞都被封装到一个Promise对象中异步执行,我想可能是因为这样await才必须在async函数中吧。
- 如果await等到的不是Promise对象的话会将它转成立即resolve的Promise对象,并将该值作为await表达式的结果(打印完”同步代码”之后立即打出”test”);如果是的话就等着Promise对象resolve(),然后将resolve的结果作为await表达式的结果(一秒后打印”999”)。
在async函数中,这样的代码看起来就像是同步代码,await等待到结果才执行后面的语句,但是却不会阻塞async函数之外的代码的执行。
async/await
刚开始可能会感觉async/await就是将Promise中的then变成了用await的方式书写,好像并没有什么优势。
我们先举个栗子:
假设一个功能需要多个步骤完成,并且每个步骤都是异步的,而且后面的步骤依赖于前面步骤的结果
先看看用Promise要怎么写:
上述三个步骤一共用时1000+1500+2000共4500ms,和console.time()/console.timeEnd()运算结果一致。
如果用async/await来写会怎么样呢?
运行结果都是一样的,但是这看起来就像同步代码,非常清晰。
再改一下需求,如果后面步骤依赖前面每一个步骤的结果,又该怎么改呢?反正用async/await修改是很方便的,只需要将前面步骤的结果当参数传递进去就行了,至于Promise的写法,好像就有点麻烦了…
另外,Promise对象不会一直是resolve啊,也会reject的,所以需要将await放在try…catch代码块中对错误进行处理
|
|
这样才不会阻塞代码的正常运行
今天就先到这吧,以后有了新的理解再补充