async/await在打包中的实现

起源

​为了在老工程中使用vue和react等,目前老项目使用rollup-to-nej这个工具打包。在观察打包生成后代代码的时候,发现有挺多的一些辅助函数,这些辅助函数会被重复的加入到我们打包生成的文件中,比如当我们使用async/await的时候。解决方法就是引入babel-runtime和babel-helper并修改babel配置来移除这些辅助函数。

当我比较修改之后的打包文件的时候,发现用到async/await的时候少了这么两个辅助函数。


那么到底打包的时候是如何实现async/await的呢?

实现

async/await实目前用的最多异步解决方案,它其实是Generator的一种语法糖,可以理解为一个自执行的generator。通常在babel或者ts打包为,一般会实现已下两步。

  • 实现一个自执行的Generator
  • 实现一个Generator(打包为es5)

实现一个自执行的Generator

通常来说我们通过已下两种方式实现:

  • 通过不断进行回调函数的执行,直到全部过程执行完毕,基于这种思路的是thunkify模块;
  • 使用Javascript原生支持的Promise对象,将异步过程扁平化处理,基于这种思路的是co模块。

babel和ts在打包的时候都是使用co模块的思路通过Promise来实现,将已下测试代码在ts中转为es5

async function doTest(asyncFn, name) {
  console.log(0)
  const result = await asyncFn(name);
  console.log(result)
  console.log(1)
  await asyncFn()
}
"use strict";
// async/await的实现
// 参数generator就是一个generator函数
// 参数P默认传void 0,取Promise
var __awaiter =
  (this && this.__awaiter) ||
  function (thisArg, _arguments, P, generator) {
    // adopt返回一个promise
    function adopt(value) {
      return value instanceof P
        ? value
        : new P(function (resolve) {
            resolve(value);
          });
    }

    return new (P || (P = Promise))(function (resolve, reject) {
      // promise的fulfilled函数,会去调用generator.next
      function fulfilled(value) {
        try {
          step(generator.next(value));
        } catch (e) {
          reject(e);
        }
      }
      // promise的rejected函数,会去调用generator.throw
      function rejected(value) {
        try {
          step(generator["throw"](value));
        } catch (e) {
          reject(e);
        }
      }
      // step判断generater函数有没有执行完
      function step(result) {
        // generater函数执行完的result值的done为true,执行完则resolve()
        // 否则生成一个Promise继续执行next
        result.done
          ? resolve(result.value)
          : adopt(result.value).then(fulfilled, rejected);
      }
      // 开始执行generator
      step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
  };
  // 原来的async和await会转换成一个generator实现
  function doTest(asyncFn, name) {
    return __awaiter(this, void 0, void 0, function* () {
        console.log(0);
        const result = yield asyncFn(name);
        console.log(result);
        console.log(1);
        yield asyncFn();
    });
}

看完ts的实现,在去看babel的实现(如图1中所示),会发现他其实就是多拆分成了一个函数而已,实现方法都是相同的。

实现一个Generator生成器

因为因为Generator是es6的新语法,所以在babel和ts中打包为es5的代码时需要去实现一个Generator生成器。生成器的强大之处在于能方便地对生成器函数内部的逻辑进行控制。在生成器函数内部,通过yieldyield*,将当前生成器函数的控制权移交给外部,外部通过调用生成器的nextthrowreturn方法将控制权返还给生成器函数,并且还能够向其传递数据。

生成器并非由引擎从底层提供额外的支持,我们可以将生成器视为一个语法糖,用一个辅助工具将生成器函数转换为普通的Javascript代码,在经过转换的代码中,有两个关键点,一是要保存函数的上下文信息,二是实现一个完善的迭代方法,使得多个yield表达式按序执行,从而实现生成器的特性。

在babel中实现一个Generator

 function _doTest() {
    _doTest = _asyncToGenerator(
    /*#__PURE__*/
    regeneratorRuntime.mark(function _callee(asyncFn, name) {
      var result;
      return regeneratorRuntime.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              console.log(0);
              _context.next = 3;
              return asyncFn(name);

            case 3:
              result = _context.sent;
              console.log(result);
              console.log(1);
              _context.next = 8;
              return asyncFn();

            case 8:
            case "end":
              return _context.stop();
          }
        }
      }, _callee);
    }));
    return _doTest.apply(this, arguments);
  }
  • yield被转换为switch case,_context保存着当前函数的上下文状态
    我们可以把switch case看做看做是一个状态机,根据_context的状态来执行不同的代码。
    函数被regeneratorRuntime.mark包装,返回一个被regeneratorRuntime.wrap包装的迭代器对象。
    简单来说,我们可以理解为实现了一个带有next\return\throw等属性的一个迭代器对象,babel中使用regenerator生成。如果了解regenerator的实现,可以看一下ES6 系列之 Babel 将 Generator 编译成了什么样子

我么也可以简单的看下ts中generator的实现。

var __generator = (this && this.__generator) || function (thisArg, body) {
    // _保存着generator的上下文状态 
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    // 返回一个遍历器对象
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    // 调用step
    function verb(n) { return function (v) { return step([n, v]); }; }
    //根据op调用,首次执行时op为[0, undefined],后续为body的返回值
    function step(op) {

        if (f) throw new TypeError("Generator is already executing.");
        // 
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                // 返回未完成
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            //每次next调用后都去执行body,_为上下文状态
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        // 执行完成  此时op[0]为2,0b10 & 0b101 为0。
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
function doTest(asyncFn, name) {
    return __awaiter(this, void 0, void 0, function () {
        var result;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    console.log(0);
                    return [4 /*yield*/, asyncFn(name)];
                case 1:
                    result = _a.sent();
                    console.log(result);
                    console.log(1);
                    return [4 /*yield*/, asyncFn()];
                case 2:
                    _a.sent();
                    return [2 /*return*/];
            }
        });
    });
}

它的实现也遵循了使用switch case来实现yield,_来保存上下文状态。在__generator函数中实现了一个迭代器对象,有着next,throw,return等方法。

猜你喜欢

转载自blog.csdn.net/weixin_44786530/article/details/130424957