async/await原理

151 阅读2分钟

基本实现

在Promise之前,我们经历着痛苦的回调地狱,但是Promise的then链式调用也存在着大量的代码嵌套,可读性不好,如要实现a -> b -> c的异步流程,用Promise需要如下写法:

  Promise.resolve(a).then(() => {
    // b code
  }).then(() => {
    // c code
  })

在Promise之后出现了Generator的方式,通过yeild的方式,把函数的执行挂起,再通过generator.next()切换到下一个状态,提供了一种优雅的异步编程解决方案,实现如上写法:

  functionmyGenerator() {
    yield Promise.resolve(1);
    yield Promise.resolve(2);
    // 注意此处 yield 和 return 的区别,return 只需执行3次next 最后value会有值,yield 4次无值
    // yield Promise.resolve(3);
    return Promise.resolve(3);
  }
  let gen = myGenerator();
  // 此时GeneratorState 为suspended
  gen.next();
  gen.next();
  gen.next(); // { value: 3, done: true }
  // 此时GeneratorState 为closed

Generator不久之后出现终级大杀器了async/await,实际上async/await就是Generator的语法糖,实现如上的功能:

  async () => {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(2);
  const c = await Promise.resolve(3);
}

但两种还是有两明显的区别:

1、async/await会自执行代码块,而生成器需要手动调用next 2、async函数返回的是Promise,而Generator返回的是生成器对象

如何让执行器自动执行

  function run(gen) {
    return new Promise((resolve, reject) => {
        let g = gen();
        function _next(val) {
          let res;
          try {
            res = g.next(val);
          } catch(e) {
            reject(e);
          }
          if (res.done) {
            return resolve(res.value)
          }
          Promise.resolve(res.value).then((val) => {
            _next(val);
          })
        }
    })
  }

async/await实现

// 编译前代码
export default async function openPage() {
  const newWindow = window.open('''_blank');
  try {
    const url = await Promise.resolve('www.baidu.com');
    newWindow.location.href = url;
  } catch (e) {
    newWindow.close();
  }
}
// 编译后代码

// 类似上文 run _next方法
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg); // 此时的info 为 invoke执行返回的{value, done}
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}
// 类似上文 run 方法
function _asyncToGenerator(fn) {
  return function() {
    var self = this,
      args = arguments;
    return new Promise(function(resolve, reject) {
      var gen = fn.apply(self, args); // fn目前绑定过GP(generator prototype)的callee
      // gen就是regeneratorRuntime.wrap生成的generator对象
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
      }
      _next(undefined);
    });
  };
}

const regeneratorRuntime = {}
// mark主要就是给生成器函数(foo)绑定了一系列原型
regeneratorRuntime.mark = function(genFun) {
  genFun.__proto__ = GeneratorFunctionPrototype;
  genFun.prototype = Object.create(Gp);
  return genFun;
}
// wrap方法先是创建了一个generator,并继承outerFn.prototype;然后new了一个context对象;makeInvokeMethod方法接收innerFn(对应foo$)、context和this,并把返回值挂到generator._invoke上;最后return了generator。
regeneratorRuntime.wrap = (innerFn, outerFn, self) => {
  var generator = Object.create(outerFn.prototype);
  var context = new Context([]);
  // 低配版context  
  // var context = {
  // next:0,
  // prev: 0,
  // done: false,
  // stop: function stop () {
  //   this.done = true
  // }
}
  generator._invoke = makeInvokeMethod(innerFn, self, context);

  return generator;
}

// 创建invoke(真正的next)方法
function makeInvokeMethod(innerFn, context) {
  // 将状态置为start
  var state = "start";

  return function invoke(method, arg) {
    // 已完成
    if (state === "completed") {
      return { value: undefined, done: true };
    }

    context.method = method;
    context.arg = arg;

    // 执行中
    while (true) {
      state = "executing";

      var record = {
        type: "normal",
        arg: innerFn.call(self, context)    // 执行下一步,并获取状态(其实就是switch里边return的值)
      };

      if (record.type === "normal") {
        // 判断是否已经执行完成
        state = context.done ? "completed" : "yield";

        // ContinueSentinel其实是一个空对象,record.arg === {}则跳过return进入下一个循环
        // 什么时候record.arg会为空对象呢, 答案是没有后续yield语句或已经return的时候,也就是switch返回了空值的情况(跟着上面的switch走一下就知道了)
        if (record.arg === ContinueSentinel) {
          continue;
        }
        // next()的返回值
        return {
          value: record.arg,
          done: context.done
        };
      }
    }
  };
}
// async/await内函数本体被转化后的代码
function _openPage() {
  _openPage = _asyncToGenerator(
    /*#__PURE__*/ regeneratorRuntime.mark(function _callee() {
      var newWindow, url;
      return regeneratorRuntime.wrap(
        function _callee¥(_context) {
          while (1) {
            switch ((_context.prev = _context.next)) {
              case 0:
                newWindow = window.open('''_blank');
                _context.prev = 1;
                _context.next = 4;
                return Promise.resolve('www.baidu.com');

              case 4:
                url = _context.sent;

                newWindow.location.href = url;

                _context.next = 11;
                break;

              case 8:
                _context.prev = 8;
                _context.t0 = _context['catch'](1);
                newWindow.close();

              case 11:
              case 'end':
                return _context.stop();
            }
          }
        },
        _callee,
        null,
        [[1, 8]],
      );
    }),
  );
  return _openPage.apply(this, arguments);
}

我们分析一下调用流程:我们定义的async函数被转化为以上代码 转化后的代码分为三大块:_callee¥(_context)由yield分割生成器函数代码而来 全局变量context对象用于储存函数执行上下文 invoke()方法定义next(),用于执行_callee¥(_context)来跳到下一步 当我们调用g.next(),就相当于调用invoke()方法,执行gen$(_context),进入switch语句,switch根据context的标识,执行对应的case块,return对应结果 当生成器函数运行到末尾(没有下一个yield或已经return),switch匹配不到对应代码块,就会return空值,这时g.next()返回{value: undefined, done: true}