Generator执行细节分析(co库底层实现原理)
```js // 单个异步任务执行,如何使用generator函数呢?
// v1: 创建一个generator函数 function* gen() { let url = 'http://www.baidu.com'; let res = yield fetch(url); console.log(res) }
// 1. 调用Generator函数,获取遍历器对象 let g = gen(); // 2. 使用next方法,执行异步任务的第一阶段,fetch(url) let res = g.next();
// 3. res 是一个Promise对象,{ value: Promise {
// v2: 如何执行下面的多个异步函数呢 function* gen() { // yiled实际上会返回一个Promise对象 var r1 = yield fetch('https://api.github.com/users/github'); var r2 = yield fetch('https://api.github.com/users/github/followers'); var r3 = yield fetch('https://api.github.com/users/github/repos');
console.log([r1.bio, r2[0].login, r3[0].full_name].join('\n'));
}
// 使用递归的方式来实现 function run(gen) { // 1. 执行自己定义的Generator函数 let g = gen(); function next(data) { let res = g.next(data);
// 执行完毕
if (res.done) return;
res.value.then(data => {
return data.json();
}).then(data => {
next(data)
});
}
// 2. 执行next函数
next();
}
// 首次执行并调用 run(gen);
// v3: 上面的代码优化 function run(gen) { // 1. 获取遍历器对象 let g = gen();
function next(data) {
let res = g.next(data);
if (res.done) return;
res.value.then(data => {
// 继续调用下一个Generator函数
next(data);
});
}
next();
}
// v4: 实现一个run函数,通用版本 function run(gen) { // 1. 获取遍历器对象 let g = gen();
// next函数
function next(data) {
let res = g.next(data);
if (res.done) return;
if (isPromise(res.value)) {
// 如果是一个Promise对象的话
res.value.then(data => {
next(data);
});
}
else {
res.value(data);
}
}
next();
}
// 可以判断一个对象是不是Promise对象 function isPromise(obj) { return typeof obj.then === 'function'; }
// v5. 上面代码继续优化 function run(gen) { let g = gen();
return new Promise((resolve, reject) => {
let res;
function next(data) {
try {
res = g.next(data);
}
catch (e) {
return reject(e);
}
if (res.done) return resolve(res.value);
let val = toPromise(res.value);
val.then(data => {
next(data);
}, err => {
reject(err);
});
}
next();
});
}
// 可以转换任意对象到一个Promise function toPromise(obj) { if (isPromise(obj)) { return obj; } else if ('function' === typeof obj) { return thunkToPromise(obj); }
return obj;
}
// 把一个函数转换为Promise对象 function thunkToPromise(fn) { return new Promise((resolve, reject) => { fn((err, res) => { if (err) return reject(err); resolve(res); }) }); }
// v6. 上面的代码进一步优化 function run(gen) {
return new Promise(function(resolve, reject) {
if (typeof gen == 'function') gen = gen();
// 如果 gen 不是一个迭代器
if (!gen || typeof gen.next !== 'function') return resolve(gen)
onFulfilled();
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise(ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise ' +
'but the following object was passed: "' + String(ret.value) + '"'));
}
})
}
function isPromise(obj) { return 'function' == typeof obj.then; }
function toPromise(obj) { if (isPromise(obj)) return obj; if ('function' == typeof obj) return thunkToPromise(obj); return obj; }
function thunkToPromise(fn) { return new Promise(function(resolve, reject) { fn(function(err, res) { if (err) return reject(err); resolve(res); }); }); }
module.exports = run;
// v7: co是什么呢? // co 是大神 TJ Holowaychuk 于 2013 年 6 月发布的一个小模块,用于 Generator 函数的自动执行。 // yield 后是一个 Promise var fetch = require('node-fetch'); var co = require('co');
function* gen() { var r1 = yield fetch('https://api.github.com/users/github'); var json1 = yield r1.json(); var r2 = yield fetch('https://api.github.com/users/github/followers'); var json2 = yield r2.json(); var r3 = yield fetch('https://api.github.com/users/github/repos'); var json3 = yield r3.json();
console.log([json1.bio, json2[0].login, json3[0].full_name].join('\n'));
}
// 可以让Generator里面的函数按顺序执行 co(gen); ```