- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第4期,链接:【若川视野 x 源码共读】第4期 | co 源码 | 觉得较难可先跳过 - 掘金 (juejin.cn)
Generator
普通函数一旦开始执行,就会运行到最后或者遇到 return 结束,运行期间不会被打断。而 Generator 是 ES6 提供的一种异步编程解决方案,可以根据 yield 关键字一步一步执行。
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator(); // "Generator { }"
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
co
可以自动执行 Generator 函数,并转换为 Promise。
const co = require("co");
function createPromise(res) {
return Promise.resolve(res).then(r => r);
}
function* generator() {
const a = yield createPromise(1);
const b = yield createPromise(2);
const c = yield createPromise(3);
console.log(a, b, c); // 1 2 3
return 123;
}
co(generator).then(res => {
console.log(res); // 123
});
简单实现
因为 Generator 函数执行后,返回的是一个 Iterator 对象,那我们可以通过 while 循环执行去实现。
function getPromiseValue(promise) {
return promise
}
async function co(gen) {
gen = gen();
let val;
while (true) {
let ret = await gen.next(val);
val = await getPromiseValue(ret.value);
if (ret.done) return val;
}
}
源码分析
接收一个 Generator 函数,返回一个 Promise 对象。
function co(gen) {
return new Promise((resolve, reject) => {})
}
作为工具库要对参数进行判断,判断 gen 是否为 Generator,如果是则执行该函数,如果不是,则结束 Promise 的执行。
function co(gen) {
const ctx = this
return new Promise((resolve, reject) => {
if (typeof gen === 'function') gen = gen.call(ctx)
if (!gen || typeof gen.next !== 'boolean') return resolve(gen)
})
}
开始轮询 Generator 函数的内部指针对象,将其包装成 onFulilled 函数。
function co(gen) {
const ctx = this;
return new Promise((resolve, reject) => {
if (typeof gen === "function") gen = gen.call(ctx);
if (!gen || typeof gen.next !== "boolean") return resolve(gen);
onFulfilled();
/**
* 开始执行下一次 yield 关键字对应的步骤
* @param {*} res 上一步执行返回的值
* @returns
*/
function onFulfilled(res) {
var ret;
try {
// 获取 yield 下一步对应的执行内容
ret = gen.next(res);
} catch (e) {
return reject(e);
}
// 开始执行
next(ret);
return null;
}
/**
* 执行 Generator 的每一步
* @param {*} ret Gernerator 的单步骤
* @returns
*/
function next(ret) {
// ret.done 为 true 标识 Gererator 执行结束,返回最后一步的 value 值
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
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) + '"'));
}
});
}
最为重要的就是 next 函数内这段代码的执行,利用 next 函数和 onFulilled 函数构成一个类递归调用的过程,不断通过更改 Gernerator 函数内部指针对象,将每一次 Gererator 函数的 next 执行对象用 toPromise 函数包装成新的 Promise,onFulfilled 作为Promise 执行成功的函数。
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
流程
总结(Promise、Generator、async对比)
Promise
Promise 是异步编程的一种解决方案,是为了解决回调函数产生的问题而诞生的,但是 Promise 无法通过 try catch 捕获错误,只可以通过 then的第二个回调或 catch 来捕获,并且 Promise 一旦新建就会立即执行,无法取消
Genertaor
Generator 是异步编程的解决方案,它不同于以往的函数一旦执行只有执行结束或者遇见 return 才会执行,而是一种可以暂停执行的函数,yield就是暂停的标志
Async
async 是依照 Genertaor 的使用方式对 Promise 做的使用扩展,是 Promise 的语法糖