1

一道打印顺序引发的血案

异步编程是 JS 中重要的模块,前几日逛 B 站,刷到这么一个提问,拿来研究一下:

let promise1 = Promise.resolve()
    .then(res => console.log(1))
    .then(res => console.log(2))

let promise2 = new Promise(resolve => {
    setTimeout(() => {
        console.log(6)
        resolve()
    })
}).then(res => console.log(3))

async function main() {
    console.log(4)
    console.log(await Promise.all([promise2, promise1]))
    console.log(5)
    return { obj: 5 }
}

let promise3 = Promise.resolve()
    .then(res => console.log(8))
    .then(res => console.log(9))

console.log(typeof main())
变量对象声明

首先会创建变量对象,声明函数和变量,函数的声明优先级比变量高。会变成

VO = {
  main: reference async function(){},
  promise1: undefined,
  promise2: undefined,
  promise3: undefined,
}

接下来 代码执行阶段,首先会对变量进行赋值:

对 promise1 来说:
Promise.resolve() 会立即执行
then(res => console.log(1)).then... 会放入本轮的微任务

对 promise2 来说
开启一个 setTimeout 宏任务,之后的 then 会在宏任务结束后的 当次 微任务 中执行

对 promise3 来说:
Promise.resolve() 会立即执行
then(res => console.log(8)).then... 会放入本轮的微任务

进入 main 函数:
执行 console.log(4) 
执行 console.log(await Promise.all([promise2, promise1])) 
这一步是整个函数最关键的地方。

async...await 执行顺序

async...await 是 generator 的语法糖。本质是 generator + co 库,内置函数执行器。会自动执行每一个 yeild,把最后执行的结果通过 Promise resolve 的方式抛出。方便链式调用。
我们把代码通过 babel 转译,可以看下面代码中注释的 1 、2 两个地方。

"use strict";

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { 
  try { 
    var info = gen[key](arg); 
    var value = info.value; 
  } catch (error) { 
    reject(error); return; 
  } 
  if (info.done) { 
    resolve(value); 
  } else { 
    Promise.resolve(value).then(_next, _throw); 
  } 
}

function _asyncToGenerator(fn) { 
  return function () { 
    var self = this, 
    args = arguments; 
    // 2. 执行 async 首先返回一个 promise
    // 返回之前会先执行完 Promise 里面的 executor 函数
    return new Promise(function (resolve, reject) { 
      var gen = fn.apply(self, args); 
      function _next(value) { 
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); 
      } 
      function _throw(err) { 
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } 
        _next(undefined); 
    });
  }; 
}

var promise1 = Promise.resolve().then(function (res) {
  return console.log(1);
}).then(function (res) {
  return console.log(2);
});
var promise2 = new Promise(function (resolve) {
  setTimeout(function () {
    console.log(6);
    resolve();
  });
}).then(function (res) {
  return console.log(3);
});

function main() {
  
  return _main.apply(this, arguments);
}

function _main() {
  // 1. main 函数开始真正执行的是 _asyncToGenerator
  _main = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
    // 3. regeneratorRuntime 函数来自 facebook 的 regenerator 模块,用来编译 generator 函数
    // mark 函数为 目标函数的原型链上注入了 next、throw、return 等函数
    // wrap 函数包装一层,把next 的调用指向外部的 context
    // 这一步核心可以看 regenerator 源码
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            console.log(4);
            _context.t0 = console;
            _context.next = 4;
            return Promise.all([promise2, promise1]);

          case 4:
            _context.t1 = _context.sent;

            _context.t0.log.call(_context.t0, _context.t1);

            console.log(5);
            return _context.abrupt("return", {
              obj: 5
            });

          case 8:
          case "end":
            return _context.stop();
        }
      }
    }, _callee);
  }));
  return _main.apply(this, arguments);
}

var promise3 = Promise.resolve().then(function (res) {
  return console.log(8);
}).then(function (res) {
  return console.log(9);
});
console.log(typeof(main()));

等待 console.log(await Promise.all([promise2, promise1])) 执行完,这一行比较有迷惑性,promise1 和 promise2 的执行前面已经触发,此处只是等他们的异步全部执行完。
之后的执行就是同步操作了

自此完整输出顺序为:

 4
'object' //async 函数返回
 1
 8
 2
 9
 6
 3
[undefined, undefined]
 5
regenerator

关于 regenerator 模块的参考资料,感兴趣的可以了解以下资料

1、https://www.iteye.com/blog/schifred-2369320
2、https://juejin.cn/post/6844903701908291592

jarbinup
105 声望1 粉丝

大力出奇迹