004 CO 函数库
CENSWIN 7/21/2021 Javascript
# 什么是 co 函数库
co 函数库 (opens new window)是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行。
比如,有一个 Generator 函数,用于依次发送两个请求
function* foo() {
let res = yield fetch("https://jsonplaceholder.typicode.com/todos/1");
console.log("res1", res);
let res2 = yield fetch("https://jsonplaceholder.typicode.com/todos/2");
console.log("res2", res2);
}
1
2
3
4
5
6
2
3
4
5
6
CO 函数就可以让开发者不用自己编写执行器
var co = require('co');
co(foo);
1
2
2
只要传入 Generator 函数就会自动执行,CO 函数会返回一个 promise 对象,因此可以用 then 方法添加回调函数。
co(foo).then(function (){
console.log('Generator 函数执行完成');
})
1
2
3
2
3
# 原理
自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。
- 回调函数。将异步操作包装成
Thunk函数,在回调函数里面交回执行权 Promise对象。将异步操作包装成Promise对象,用 then 方法交回执行权
使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象
# 基于 Promise 对象的自动执行
沿用上面的例子,我们来手动执行这个 Generator 函数
function* foo() {
let res = yield fetch("https://jsonplaceholder.typicode.com/todos/1").then(res => res.json());
console.log("res1", res);
let res2 = yield fetch("https://jsonplaceholder.typicode.com/todos/2").then(res => res.json());
console.log("res2", res2);
}
1
2
3
4
5
6
2
3
4
5
6
手动执行上面的 Generator 函数
const gen = foo()
gen.next().value.then(res => {
gen.next(res).value.then(res => {
gen.next(res)
})
})
1
2
3
4
5
6
2
3
4
5
6
自动执行代码
function run(gen) {
const g = gen()
function next(data) {
const res = g.next(data)
if (res.done) {
return
}
res.value.then(data => {
next(data)
})
}
next()
}
run(foo);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
co 就是上面那个自动执行器的扩展 源码 (opens new window)
function co(gen) {
var ctx = this;
return new Promise(function (resolve, reject) {
if (typeof gen === 'function') gen = gen.call(ctx);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
// co 将 Generator 函数的内部指针对象的 next 方法,包装成 onFulefilled 函数。这主要是为了能够捕捉抛出的错误
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
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) + '"'));
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
最后,就是关键的 next 函数,它会反复调用自身,next 函数的内部代码,一共只有四行命令
- 检查当前是否为 Generator 函数的最后一步,如果是就返回
- 确保每一步的返回值,是 Promise 对象
- 使用 then 方法,为返回值加上回调函数,然后通过 onFulfilled 函数再次调用 next 函数
- 参数不符合要求的情况下(参数非 Thunk 函数和 Promise 对象),将 Promise 对象的状态改为 rejected,从而终止执行
# 并发
co 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步,这时,要把并发的操作都放在数组或对象里面
// 数组的写法
co(function* () {
var res = yield [
Promise.resolve(1),
Promise.resolve(2)
];
console.log(res);
}).catch(onerror);
// 对象的写法
co(function* () {
var res = yield {
1: Promise.resolve(1),
2: Promise.resolve(2),
};
console.log(res);
}).catch(onerror);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
参考