sailei1 / blog

1 stars 0 forks source link

generator 理解 #39

Closed sailei1 closed 5 years ago

sailei1 commented 5 years ago

generator 转换 https://babeljs.io/repl/ 转换后代码

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _default = {
  data: function data() {
    return {};
  },
  props: {},
  components: {},
  methods: {
    checkStatus: function checkStatus() {
      var me = this;
      var ajax =
      /*#__PURE__*/
      regeneratorRuntime.mark(function ajax() {
        return regeneratorRuntime.wrap(function ajax$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.next = 2;
                return new Promise(function (resolve, reject) {
                  setTimeout(function () {
                    resolve(res);
                  }, 5000);
                });

              case 2:
              case "end":
                return _context.stop();
            }
          }
        }, ajax);
      });

      var pull = function pull() {
        var generator = ajax();
        var step = generator.next();
        step.value.then(function (res) {
          if (res.code == 0) {
            var bind_status = res.data.bind_status;

            if (bind_status == 3) {
              me.status_type = 'checking';
              setTimeout(function () {
                pull();
              }, 3000);
            } else {
              me.status_type = 'success';
              me.bind_status = bind_status;
            }
          }
        });
      };

      pull();
    }
  }
};
exports.default = _default;

Javascript执行引擎仍然是一个基于事件循环的单线程环境,当生成器运行的时候,它会在叫做 caller 的同一个线程中运行。执行的顺序是有序、确定的,并且永远不会产生并发

Regenerator将生成器函数中的yield表达式重写为switch case,同时,在每个case中使用ajax$来保存函数当前的上下文状态。

通过mark包装function

var Gp = GeneratorFunctionPrototype.prototype =
    Generator.prototype = Object.create(IteratorPrototype);
  GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
  GeneratorFunctionPrototype.constructor = GeneratorFunction;
  GeneratorFunctionPrototype[toStringTagSymbol] =
    GeneratorFunction.displayName = "GeneratorFunction";

regeneratorRuntime.mark=function(genFun) {
  if (Object.setPrototypeOf) {
    Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
  } else {
    genFun.__proto__ = GeneratorFunctionPrototype;
  }
  genFun.prototype = Object.create(Gp);
  return genFun;
};

当调用生成器函数ajax()时,返回一个被wrap函数包装后的迭代器对象

regeneratorRuntime.wrap=function (innerFn, outerFn, self, tryLocsList) {
  // If outerFn provided, then outerFn.prototype instanceof Generator.
  var generator = Object.create((outerFn || Generator).prototype);
  var context = new Context(tryLocsList || []);

  // The ._invoke method unifies the implementations of the .next,
  // .throw, and .return methods.
  generator._invoke = makeInvokeMethod(innerFn, self, context);

  return generator;
}
// Helper for defining the .next, .throw, and .return methods of the
// Iterator interface in terms of a single ._invoke method.
function defineIteratorMethods(prototype) {
  ["next", "throw", "return"].forEach(function(method) {
    prototype[method] = function(arg) {
      return this._invoke(method, arg);
    };
  });
}

当调用迭代器对象generator.next()方法时,因为有如下代码,所以会执行_invoke方法,而根据前面wrap方法代码可知,最终是调用了迭代器对象的makeInvokeMethod (innerFn, self, context);方法

Regenerator通过工具函数将生成器函数包装,为其添加如next/return等方法。同时也对返回的生成器对象进行包装,使得对next等方法的调用,最终进入由switch case组成的状态机模型中。除此之外,利用闭包技巧,保存生成器函数上下文信息。

感谢 http://www.alloyteam.com/2016/02/generators-in-depth/ https://github.com/facebook/regenerator/blob/master/packages/regenerator-runtime/runtime.js