利用generator模拟实现async(分析co源码)

最近看了下co的源码,它利用generator实现了更好的一种异步编程方式,用起来的感觉很像async,理解了co可以对async语法糖的本质有更深的见解,下面我们分析下它的源码

先看下使用方式:

co(function *(){
  // 顺序执行,可以将这里的yield看作是await
  // 但是yield后面只能是promise、function、object、array
  var result1 = yield Promise.resolve(true);
  var result2 = yield Promise.resolve(false);
}).catch(onerror);

co(function *(){
  // yield后面可以数组,
  // 并发执行数组里的每一项,
  //并且每一项的输出结果与数组的每一项对应
  var a = Promise.resolve(1);
  var b = Promise.resolve(2);
  var c = Promise.resolve(3);
  var res = yield [a, b, c];
  console.log(res);
  // => [1, 2, 3]
}).catch(onerror);

co(function* () {
  // yield后面可以对象,
  // 并发执行对象里的每一项,
  //并且每一项的输出结果与对象的每一项对应
  var res = yield {
    1: Promise.resolve(1),
    2: Promise.resolve(2),
  };
  console.log(res); // => { 1: 1, 2: 2 }
}).catch(onerror);

co(function *() {
  // co也支持thunk函数
  var res = yield function thunk(cb) {
    callSomething(args, cb)
  }
})
复制代码

然后我们看下源码:

var slice = Array.prototype.slice;

function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1);

  // 调用co会返回一个promise
  // 当所有yield执行完成后被resolve
  // resolve的值为generator的返回值
  return new Promise(function(resolve, reject) {
    // 第一个参数为generator或者是迭代器(generator的返回值是一个迭代器)
    // 如果是generator,则调用得到迭代器
    // 如果既不是生成器又不是迭代器则直接被reolve
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    // 开始迭代
    onFulfilled();

    function onFulfilled(res) {
      var ret;
      // 调用迭代器的next()方法,若在调用过程出错则直接reject
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      // 开始下一次迭代
      next(ret);
      return null;
    }

    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    function next(ret) {
      // 如果yield执行结束则直接reolve
      if (ret.done) return resolve(ret.value);
      // co的本质是将yield后的任何类型都转换为promise
      // toPromise则是转换为promise的方法
      // 因此,value即是一个promise
      var value = toPromise.call(ctx, ret.value);
      // 在value的回调里注册onFulfilled与onRejected,
      // 所以只有当promise的状态变为非pending时才会开始下一次迭代
      // 这里是co利用generatro实现异步编程的核心
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      // 如果yield后面不是function,promise,array,object则不会被toPromise转换为promise
      // 如果yield后面不是所期望的类型,则直接reject
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));
    }
  });
}

function toPromise(obj) {
  if (!obj) return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ('function' == typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}

// thunk函数转换为promise
function thunkToPromise(fn) {
  var ctx = this;
  return new Promise(function (resolve, reject) {
    fn.call(ctx, function (err, res) {
      if (err) return reject(err);
      if (arguments.length > 2) res = slice.call(arguments, 1);
      resolve(res);
    });
  });
}

// 数组转换为promise,数组里的每一项也必须符合yield的要求
function arrayToPromise(obj) {
  return Promise.all(obj.map(toPromise, this));
}

// 对象转换为promise
function objectToPromise(obj){
  var results = new obj.constructor();
  var keys = Object.keys(obj);
  var promises = [];
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var promise = toPromise.call(this, obj[key]);
    if (promise && isPromise(promise)) defer(promise, key);
    else results[key] = obj[key];
  }
  return Promise.all(promises).then(function () {
    return results;
  });

  function defer(promise, key) {
    // predefine the key in the result
    results[key] = undefined;
    promises.push(promise.then(function (res) {
      results[key] = res;
    }));
  }
}
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值