async/await中的await小结

由于学习Node.js写后台代码,那么必不可少要学习一下async/await。顺其自然的就找到了阮一峰大师的ES6文档,翻阅了其async这一模块内容来学习。主要来说说await吧

Await 了解过程

  1. 初步了解:
    1. 来源:阮一峰版本ES6入门文档
    2. async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
    3. await命令类似于Promise对象then的语法糖
  2. 深究:
    1. 辅助查询:Job queue 和 event loop
    2. await 命令后面若不是Promise对象,但是await命令后的函数体内执行了promise会是什么样。

基于上面的三点的内容让我对Await有了最初的了解,当然还是不太清楚的。通过JS-Bin边打印,边测试。还不是很清楚async/await的好处,虽然文档明明白白的写着以同步代码的方式来写异步代码。没有去理解async异步,await等待的意思。就开始写Node.js了。刚好我用到的数据库处理模块是sequelize,基于bluebird的promise对象写的。这就为我之后苦苦寻找await到底是如果判断等待结束标志埋下了种子。

Part.1 

导火索是类似于下面的代码

const blueBird= require("bluebird");// bluebird对象

async function test(){
    await blue();
    await native();
    console.log("END");

}


// 原生Promise对象调用方法
function native(){
    new Promise(r=>{r(2)})
        .then(r=>{
            console.log(`native:${r}`);
        })
}


// Bluebird对象调用方法
function blue(){
    new blueBird((r)=>{r(2);})
        .then(r=>{
            console.log(`blue:${r}`);
        });
}

test();

上面代码运行后的结果是: native : 2      END     blue : 2

首先我的疑惑是为什么bluebird的promise的回调会在最后执行呢?

这里需要查询的知识点有bluebird的异步是如何实现的,事件循环与任务队列两部分内容,await属于什么任务队列。

经查询后,

  1. bluebird的回调操作属于宏任务,网上查阅非官网资料说是基于setTimeout来进行设计的(有兴趣可以查找更多的资料进行考证)。
  2. async/await本质上是基于Promise的一种封装,属于微任务队列。

基于以上两点,能理解了为什么blue:2一定在最后执行,因为宏任务队列的执行顺序在微任务之后。

Part.2

现在令我疑惑的是为什么native:2会在END之前呢?

await 后面的表达式返回的不是Promise对象,是undefined。那么按道理native的主线程代码走完以后,就应该await等待结束了。根据我已经获得的知识,不能解释。按照我目前的理解是,

test开始 =>  blue执行 => blue中的bluebird对象回调加入宏任务队列 ,执行结束 => blue返回undefined,blue结束,第一个await等待结束 =>  native执行 => native中的promise对象的回调加入微任务队列 => native返回undefined,native结束,第二个await等待结束 => 打印"END" => test结束 => 执行微队列中的promise回调,打印得到"native : 2" =>  执行宏任务队列中的bluebird回调,打印得到"blue:2"。

为什么!?native:2 会在END之前打印呢? 不解决真的令我心里有结。在与技术交流群中的兄弟们讨论了后,终于豁然开朗了。令我这么迷惑最根本的原因是关于await有一句关键的话我没有看到:

红色箭头的话什么意思?意思就是await后面如果跟的不是Promise,那么await也会将其包装成Promise来处理,加入promise队列。赶紧用代码来验证一下。


async function test(){
  new Promise(resolve=>{resolve(2)})
  .then(r=>{
    console.log(`test is ${r}`);
  }).then(r=>{
    console.log(`test is 3`);
  });
  await native();
  console.log("END");
}

function native(){
  new Promise(resolve=>{
    resolve(2);
  }).then(r=>{
    console.log(`native is ${r}`);
  })
}

test();

和我猜想的一样。处理的顺序应该是

主线程:

test开始 => 第一个promise对象执行完毕,回调加入微任务队列 => native开始执行 => native执行结束,回调加入微队列 => await后面的native返回undefined,await将其包装成promise,并加入微队列,await等待未结束 

第一次事件循环:

  1. 将微队列中第一个任务拿出,打印出"test is 2" ,并将第一个promise的第二个then操作产生一个微任务,加入微任务队列最后
  2. 将微任务队列中第二个任务拿出,打印" native is 2"
  3. 将微任务队列中的第三个任务拿出,await等待结束,打印"END",异步方法test执行结束
  4. 将最后一个微任务拿出,打印" test is 3" 

这样解释就通了,为了理清这些内容,向群里的朋友们请教,网上也查了写资料,但是发现大多数的帖子的内容都是大同小异,所以将自己的一些发现写做博客,希望可以给和我一样遇到这个问题而不解的朋友做个参考。

以上内容,如有不对之处,请帮忙纠正,谢谢!

猜你喜欢

转载自blog.csdn.net/qq_37028216/article/details/84255761