Generator执行细节分析(co库底层实现原理)
// 单个异步任务执行,如何使用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 { <pending> }, done: false }
res.value.then(data => {
// 4. 将获取到的数据格式化
return data.json();
}).then(data => {
// 5. 然后将格式化之后的数据传进去,调用next函数,会执行输入console.log(res)
g.next(data);
})
// 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);
评论
填写昵称与邮箱即可评论,无需登录。