Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

co函数库源码解析 #18

Open
hawx1993 opened this issue Jul 7, 2017 · 0 comments
Open

co函数库源码解析 #18

hawx1993 opened this issue Jul 7, 2017 · 0 comments

Comments

@hawx1993
Copy link
Owner

hawx1993 commented Jul 7, 2017

co 函数库是一个中间产品,co是coroutine的缩写,即协同程序。co可以说是给generator增加了promise实现。co是利用Generator的方式实现了async/await

co函数库对外暴露的方法只有两个:

  • co(fn *)
  • co.wrap(fn *)

其中,fn *代表Generator函数。看一个官方文档中的例子:

co(function* () {
  return yield Promise.resolve(true);
}).then(function (val) {
  console.log(val);
}, function (err) {
  console.error(err.stack);
});

co函数接收一个Generator 函数作为参数。执行co函数的时候,生成器函数内部的逻辑像async函数调用时一样被执行。不同之处只是这里的await变成了yield(产出)。async和co一样,都返回了Promise对象,都可以链式调用.then()方法

关于Generator 函数,我们可以看一个例子:

function* generatorFunction() {
    console.log(1);
    var a = yield 'a';
    console.log(2);
    var b = yield 'b';
    console.log(3);
    var c = yield 'c';
    return c;
}

var gen = generatorFunction();
gen.next();    // 打印 1,返回 {value: "a", done: false}
gen.next();    // 打印 2,返回 {value: "b", done: false}
gen.next();    // 打印 3,返回 {value: "c", done: false}
gen.next('d'); // 没有打印,返回 {value: "d", done: true}
gen.next();    // 没有打印,返回 {value: undefined, done: true}

gen.next()的返回对象格式如下:

{
   done: {boolean},
   value: {*}
}

done 表示迭代器是否结束,value 表示yield后面语句的返回值。co用到的一些特性:

  • gen.next()和yield配合使用,以yield为界分段执行
  • gen.next(value)用异步操作结果(value)改写前一个yield的 value
  • gen.throw(error)抛出错误可在generator内部捕获

co内部定义了co函数,通过返回new Promise()对象,实现了链式调用。

一个Generator内部可能有多个yield,一个yield结束之后会执行下一个yield,这个过程通过递归实现:

function onFulfilled(res) {
      var ret
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
      return null
    }

reject则通过throw来将错误抛出

对于next函数的实现:

//获取generator next的值,并返回Promise
function next(ret) {
      if (ret.done) return resolve(ret.value);//如果迭代器结束,则将值作为resolve的参数传递出去,可以在.then中获取到
      var value = toPromise.call(ctx, ret.value);//ctx代表当前环境对象,window or global
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));
    }
//将yield产出的值转为Promise的值
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;
}

所以其实co函数库只是内部封装了Generator函数和自动执行器(通过递归调用实现),并返回了Promise对象。使其可以和async一样,做到“同步调用,异步执行”

co.wrap(fn *)

传入一个generator,返回一个普通函数,这个函数会返回一个Promise。

co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;//[GeneratorFunction: callee]

  return createPromise;
  function createPromise() {
    return co.call(this, fn.apply(this, arguments));
  }
};

由于声明式函数具有提升作用,所以createPromise可以在上面调用,而createPromise函数则是:

{ [Function: createPromise] __generatorFunction__: [GeneratorFunction: callee$0$0] }

其中,fn.apply(this, arguments)则是:

GeneratorFunctionPrototype { _invoke: [Function: invoke] }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant