Generator 是 ES6 标准中的异步编程方式
通俗来讲 Generator 是一个带星号的“函数”(它并不是真正的函数,下面的代码会为你验证),可以配合 yield 关键字来暂停或者执行函数。
Generator(生成器)
Generator 也是一种异步编程解决方案,它最大的特点就是可以交出函数的执行权,
Generator 函数可以看出是异步任务的容器,需要暂停的地方,都用 yield 语法来标注。
Generator 函数一般配合 yield 使用,Generator 函数最后返回的是迭代器
function* gen() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = gen();
t.next(1); // 第一次调用next函数时,传递的参数无效,故无打印结果 执行到 let a = yield 111; 即结束,console.log(a);未执行,所以未打印
t.next(2); // a输出2;
t.next(3); // b输出3;
t.next(4); // c输出4;
t.next(5); // d输出5;
// 第一次的 next 虽然执行了但是并未输出结果,后面的每次执行 next 会把参数传入然后打印出来,
// 等到最后一次 next 对应的 yield 执行完之后,控制台会打印 “{value: undefined, done: true}” 的输出结果,
// 标识该 Generator 函数已经执行完毕,即 done:true
function* gen() {
console.log('enter');
let a = yield 1;
let b = yield (function () {
return 2;
})();
return 3;
}
var g = gen(); // 阻塞住,不会执行任何语句
console.log(typeof g); // 返回 object 这里不是 "function"
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
// output:
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: true }
// { value: undefined, done: true }
Yield
yield 同样也是 ES6 的新关键词,配合 Generator 执行以及暂停。
yield 关键词最后返回一个迭代器对象,该对象有 value 和 done 两个属性,其中 done 属性代表返回值以及是否完成。
yield 配合着 Generator,再同时使用 next 方法,可以主动控制 Generator 执行进度。
function* gen1() {
yield 1;
yield* gen2();
yield 4;
}
function* gen2() {
yield 2;
yield 3;
}
var g = gen1();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
// output:
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 4, done: false }
// {value: undefined, done: true}
// 使用 yield 关键词的话还可以配合着 Generator 函数嵌套使用,从而控制函数执行进度。
// 这样对于 Generator 的使用,以及最终函数的执行进度都可以很好地控制,从而形成符合你设想的执行顺序。
// 即便 Generator 函数相互嵌套,也能通过调用 next 方法来按照进度一步步执行
Thunk 函数
接收一定的参数,会生产出定制化的函数,最后使用定制化的函数去完成想要实现的功能
let isString = (obj) => {
return Object.prototype.toString.call(obj) === '[object String]';
};
let isFunction = (obj) => {
return Object.prototype.toString.call(obj) === '[object Function]';
};
let isArray = (obj) => {
return Object.prototype.toString.call(obj) === '[object Array]';
};
let isType = (type) => {
return (obj) => {
return Object.prototype.toString.call(obj) === `[object ${
type}]`;
};
};
let isString = isType('String');
let isArray = isType('Array');
isString('123'); // true
isArray([1, 2, 3]); // true
Generator 和 thunk 结合
const readFileThunk = (filename) => {
return (callback) => {
fs.readFile(filename, callback);
};
};
const gen = function* () {
const data1 = yield readFileThunk('1.txt');
console.log(data1.toString());
const data2 = yield readFileThunk('2.txt');
console.log(data2.toString);
};
let g = gen();
g.next().value((err, data1) => {
g.next(data1).value((err, data2) => {
g.next(data2);
});
});
// 很多层的嵌套,可读性不强
// 改进后
function run(gen) {
const next = (err, data) => {
let res = gen.next(data);
if (res.done) return;
res.value(next);
};
next();
}
run(g);
Generator 和 Promise 结合
// 最后包装成 Promise 对象进行返回
const readFilePromise = (filename) => {
return new Promise((resolve, reject) => {
fs.readFile(filename, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
}).then((res) => res);
};
// 这块和上面 thunk 的方式一样
const gen = function* () {
const data1 = yield readFilePromise('1.txt');
console.log(data1.toString());
const data2 = yield readFilePromise('2.txt');
console.log(data2.toString);
};
// 这块和上面 thunk 的方式一样
function run(gen) {
const next = (err, data) => {
let res = gen.next(data);
if (res.done) return;
res.value.then(next);
};
next();
}
run(g);
co 函数库
co 函数库是著名程序员 TJ 发布的一个小工具,用于处理 Generator 函数的自动执行。
核心原理其实就是上面讲的通过和 thunk 函数以及 Promise 对象进行配合,包装成一个库.
co 函数库可以自动执行 Generator 函数,它的处理原理是什么呢?
1.因为 Generator 函数就是一个异步操作的容器,它需要一种自动执行机制,co 函数接受 Generator 函数作为参数,并最后返回一个 Promise 对象。
2.在返回的 Promise 对象里面,co 先检查参数 gen 是否为 Generator 函数。如果是,就执行该函数;如果不是就返回,并将 Promise 对象的状态改为 resolved。
3.co 将 Generator 函数的内部指针对象的 next 方法,包装成 onFulfilled 函数。这主要是为了能够捕捉抛出的错误。
4.关键的是 next 函数,它会反复调用自身。```