关于async/await 和 promise的一些问题
前几天在fcc群里旁观水友对一个异步请求的问题的争论,争论的内容不是重点,但他说的一句话引起了我的求知欲
用 async/await 异步转同步
之前我的认知是,这是一个generator的语法糖,是用来解决异步问题的,看起来写起来是同步代码,但实际运行还是异步的,感觉和他说的有点偏差,有偏差就说明至少有个人错了;于是我打算重新认识一遍这个东西,说不定会有新收获。
TALK IS CHEAP, SHOW YOU THE CODE.
async function async1(){ console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2(){ console.log('async2'); } console.log('script start'); setTimeout(function(){ console.log('setTimeout'); },0); async1(); new Promise(function(resolve){ console.log('promise1'); resolve(); }).then(function(){ console.log('promise2'); }); console.log('script end');
按照我之前的理解,我会把答案写成:
- script start
- async1 start
- async2
- async1 end
- promise1
- script end
- promise2
- setTimeout
因为我想await async2这里就是会等待他返回然后再往下执行
然而其实答案是:
- script start
- async1 start
- async2
- promise1
- script end
- promise2
- async1 end
- setTimeout
这里就涉及到await的特点了:
await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。
这里的暂停执行,我们应该理解为交出线程控制权,去执行后面的函数。我们反向来看,假如async函数里面一直等待await后面的表达式返回,那整个线程就等于卡在这个async函数里了,那不就变成同步阻塞的了。
所以我们回到上面的那段代码,开始分析
首先代码同步执行输出
script start,接着
setTimeout中的回调属于
MacroTask队列,会被推入该队列等待执行。
接着代码执行到
async1函数里面,输出
async1 start,接着遇到了
await关键字,这一行语句会从右到左执行,先执行
async2函数,输出
async2,因为这个函数没有返回值,所以返回了一个
Promise.resolve(undefined).然后这个
Promise会被推入
MicroTask队列,同时
await让出线程,跳出
async1函数,继续执行下面的同步代码。
Promise构造器中的函数参数会被立即执行,所以输出
promise1,然后执行了
resolve(),这个
Promise也被推入
MicroTask队列等待执行,接着输出
script end,好了,同步代码执行完了,调用栈现在空了。
然后开始处理异步的任务队列。第一个是
async2中返回的
Promise.resolve(undefined)被resolve之后再次加入队列,所以回调没被执行,第二个是
new Promise里resolve的
Promise,其回调被执行,输出
promise2。好的现在调用栈又空了,然后开始再一次处理异步队列,这次取到的是之前被第二次放进来的
Promise的回调,执行之后, 表达式
await async2()的求值就完成了,值是undefined。接着可以认为
async2返回的
Promise已经处理完了,接着执行下面的代码,输出
async1 end。
最后就是
MacroTask队列中的回调被执行,输出
setTimeout
这里需要解释一下一个地方,async2 返回的Promise状态是Resolved,为什么会被放到任务队列里面两次。首先async2函数是一个
async function,他的返回值是一个resolved状态的Promise,
await async2()类似于
await Promise.resolve(something),Chrome的V8引擎对这个过程进行了优化,把Promise.resolve()的返回值优化成了返回一个新的Promise,这样他的回调就不会被立即执行,而是需要再等一个循环才执行。而在Node里面,Promise.resolve返回的就是当前的Promise,回调会被立即执行,所以如果你在node里面执行的话,顺序会是这样的:
- script start
- async1 start
- async2
- promise1
- script end
- async1 end 27aec
- promise2
- setTimeout
是因为Node环境下,async2返回的Promise在第一次被推入队列时就是resolved的,跟new Promise那个是一样的,所以按照入队列的顺序,先输出
async1 end。这一点差异,应该以最新V8引擎为准。
详细解释可以参考:
async/await 在chrome 环境和 node 环境的 执行结果不一致,求解?
好了,就这样。
- 关于async和await的一些误区实例详解
- 关于 gulp-istanbul 无法解析 async/await 的问题
- 详解koa2学习中使用 async 、await、promise解决异步的问题
- 关于async和await的一些误区
- async/await与promise(nodejs中的异步操作问题)
- 关于async和await的一些误区实例详解
- 关于async和await的一些误区实例详解
- 关于Promise与async/await的例子
- 关于 ASP.NET Web 应用中 async/await 注意问题
- 第二十三节: EF性能篇(三)之基于开源组件 Z.EntityFrameWork.Plus.EF6解决EF性能问题 第四节:一些指令总结 定时调度系列之Quartz.Net详解 第十七节:易混淆的概念(静态和非静态、拆箱和装箱) 那些年我们一起追逐的多线程(Thread、ThreadPool、委托异步调用、Task/TaskFactory、Parallerl、async和await)
- 关于async/await、promise和setTimeout执行顺序
- 关于Promise,Generator,async / await 对异步的处理
- 关于async和await的一些误区实例详解
- 关于大小端的一些问题
- 关于一些基础的Java问题的解答(七)
- async/await基本理解及项目案例(结合Promise)
- 关于Linux网络程序的一些小问题
- 关于继承的一些问题
- 关于 IE firefox Chrome下的通过用js 关闭窗口的一些问题
- 关于ASP的一些问题