最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Babel的奇妙冒险 @babel/plugin-transform-async-to-generator

    正文概述 掘金(NickNa)   2020-12-03   569

    预备知识

    • Promise
    • Generator
    • 生成器函数
    • async函数
    • 选择正确的JavaScript异步方法

    async/await 规则

    • await 必须出现在 async 声明的函数后面
    • await 后面的值如果是一个 Promise,await 将等待 Promise 正常处理完成并返回其处理结果
    • await 后面的值如果不是一个 Promise,await 会把该值转换为已正常处理的Promise,然后等待其处理结果
    • await 后面的 Promise 的状态 pending -> fulfilled 后, await 后面的代码会放在微任务队列中

    Generator 函数和自动执行器表示 async/await

    async函数文档中以后这样一句话:

    这里的“好像搭配使用了生成器和promise”怎么理解呢?我在网上找到了下面这段代码,很好的解释了这里的“搭配使用了生成器和promise”

    async function fn(args){
      // await ...
      // await ...
      // await ...
    }
    
    // 等同于
    function fn(args){
      function spawn(genF) {
        return new Promise(function(resolve, reject) {
            var gen = genF();
            function step(nextF) {
                try {
                    var next = nextF();
                } catch(e) {
                    return reject(e);
                }
                if(next.done) {
                    return resolve(next.value);
                }
                Promise.resolve(next.value).then(function(v) {
                    step(function() {
                        return gen.next(v);
                    });
                }, function(e) {
                    step(function() {
                        return gen.throw(e);
                    });
                });
            }
            step(function() {
                return gen.next(undefined);
            });
        });
      }
    
      return spawn(function*() {
        // yield ...
        // yield ...
        // yield ...
      });
    }
    fn()
    

    这段代码的思路是:将Generator 函数和自动执行器,包装在一个函数里。

    1. fn 函数返回一个新的函数 spawn 的执行结果;
    2. spawn 函数参数是一个 生成器函数
    3. spawn 返回一个 Promise, 对应与 async 函数返回一个 Promise;
    let hello = async () => { return "Hello" };
    hello().then((value) => console.log(value))
    // output: Hello
    
    let helloWorld = async () => {
        await "Hello"
        await "world"
        return "Nick"
    };
    helloWorld().then((value) => console.log(value))
    // output: Nick
    
    1. spawn 内部创建了一个生成器 var gen = genF();
    2. spawn 会执行 step 函数, 参数是一个 function, nextF;
    • 首次执行, next(undefined), 表示不向生成器传值
    • 如果生成器没有执行结束, step 会把当前生成器产生的值, 即 yield 的返回值通过 Promise.resolve 封装为 Promise
      • 如果 yield 的返回值是Promise,返回原Promise
      • 如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态
      • 否则返回的promise将以此值完成
    • 当 Promise 为完成状态时,会把 yield 后面的代码放在微任务队列
    • 当执行到该微任务时, 再次执行next方法, 把上次产生的值放传递给生成器
    • 循环执行, 直到生成器结束

    这样就用 Generator 函数和自动执行器产生了 async/await 的执行效果, 所以也可以说 async/await 是 Generator 的语法糖

    源码解析

    AST spec

    interface Function <: Node {
      id: Identifier | null;
      params: [ Pattern ];
      body: BlockStatement;
      generator: boolean;
      async: boolean;
    }
    

    插件代码

    export default declare((api, options) => {
      api.assertVersion(7);
    
      const { method, module } = options;
    
      if (method && module) {
        return {
          name: "transform-async-to-generator",
    
          visitor: {
            Function(path, state) {
              if (!path.node.async || path.node.generator) return;
    
              let wrapAsync = state.methodWrapper;
              if (wrapAsync) {
                wrapAsync = t.cloneNode(wrapAsync);
              } else {
                wrapAsync = state.methodWrapper = addNamed(path, method, module);
              }
    
              remapAsyncToGenerator(path, { wrapAsync });
            },
          },
        };
      }
    
      return {
        name: "transform-async-to-generator",
    
        visitor: {
          Function(path, state) {
            if (!path.node.async || path.node.generator) return;
    
            remapAsyncToGenerator(path, {
              wrapAsync: state.addHelper("asyncToGenerator"),
            });
          },
        },
      };
    });
    
    // remapAsyncToGenerator 部分代码
    export default function (
      path: NodePath,
      helpers: { wrapAsync: Object, wrapAwait: Object },
    ) {
      path.traverse(awaitVisitor, {
        wrapAwait: helpers.wrapAwait,
      });
    
      const isIIFE = checkIsIIFE(path);
    
      path.node.async = false;
      path.node.generator = true;
    
      wrapFunction(path, t.cloneNode(helpers.wrapAsync));
    
      const isProperty =
        path.isObjectMethod() ||
        path.isClassMethod() ||
        path.parentPath.isObjectProperty() ||
        path.parentPath.isClassProperty();
    
      if (!isProperty && !isIIFE && path.isExpression()) {
        annotateAsPure(path);
      }
    }
    

    代码主要工作先通过 !path.node.async || path.node.generator 判断如果不是 async 声明的函数或者是 generator 函数则返回. 否则调用 babel-helper-remap-async-to-generator 将 async 转为 generator, 如果是立即执行函数, 会有额外的处理.

    UT

    // input
    (async function() { await 'ok' })();
    (async function notIIFE() { await 'ok' });
    
    // output
    babelHelpers.asyncToGenerator(function* () {
      yield 'ok';
    })();
    
    /*#__PURE__*/
    (function () {
      var _notIIFE = babelHelpers.asyncToGenerator(function* () {
        yield 'ok';
      });
    
      function notIIFE() {
        return _notIIFE.apply(this, arguments);
      }
    
      return notIIFE;
    })();
    

    从 UT 可以看出 async/await 被转成了 babelHelpers.asyncToGenerator 再看一下 asyncToGenerator 长什么样子:

    helpers.asyncToGenerator = helper("7.0.0-beta.0")`
      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);
        }
      }
    
      export default function _asyncToGenerator(fn) {
        return function () {
          var self = this, args = arguments;
          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);
          });
        };
      }
    `;
    

    和上面 Generator 函数和自动执行器表示 async/await的代码如出一辙.

    小结

    async/await 的目的为了简化使用基于promise的API时所需的语法, 可以说是 Generator 的语法糖。 babel 通过 plugin-transform-async-to-generator 将 async/await 转为 Generator和自动执行器的方式。


    起源地下载网 » Babel的奇妙冒险 @babel/plugin-transform-async-to-generator

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元