您的位置:首页 > 大数据 > 人工智能

关于async/await 和 promise的一些问题

2018-12-23 16:29 375 查看

前几天在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');

按照我之前的理解,我会把答案写成:

  1. script start
  2. async1 start
  3. async2
  4. async1 end
  5. promise1
  6. script end
  7. promise2
  8. setTimeout

因为我想await async2这里就是会等待他返回然后再往下执行

然而其实答案是:

  1. script start
  2. async1 start
  3. async2
  4. promise1
  5. script end
  6. promise2
  7. async1 end
  8. 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里面执行的话,顺序会是这样的:

  1. script start
  2. async1 start
  3. async2
  4. promise1
  5. script end
  6. async1 end
  7. 27aec
  8. promise2
  9. setTimeout

是因为Node环境下,async2返回的Promise在第一次被推入队列时就是resolved的,跟new Promise那个是一样的,所以按照入队列的顺序,先输出

async1 end
。这一点差异,应该以最新V8引擎为准。

详细解释可以参考:
async/await 在chrome 环境和 node 环境的 执行结果不一致,求解?

好了,就这样。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: