NPM-异步控制模块co

异步控制模块co是处理异步的重要模块,也可用es7的async/await代替。两者各有优点。目前推荐使用原生async

什么是co

co是一个函数库,用于generator的自动执行。 我们先定义一个generator,用来异步读下面几个文件

1
2
3
4
5
6
7
8
const gen=function *(){

const b=yield read('b.js')
console.log(b.length)

const c=yield read('c.js')
console.log(c.length)
}

其中,我们定义的read异步读取文件的函数为:

1
2
3
4
5
6
7
8
function read(file){
fs.readFile(file,'utf8',(err,res)=>{
g.next(res)
})
}

const g=gen()
g.next() //将分别输出两个文件的长度

co的使用

虽然上面的代码也可以顺利异步执行generator函数,但是需要把next()写到异步函数中, 增加了耦合度,而且还要创建generator的实例,代码很繁琐。 我们使用co重构上面的代码: 首先要改造一下异步函数的结构,co规定所有的异步函数必须是偏函数,称之为thunk函数:

1
2
3
4
5
function read(file){
return (fn)=>{
fs.readFile(file,'utf8',fn)
}
}

之后,只要把generator传到co中就大功告成了

1
2
3
const co=require('co')

co(gen)() //分别输出两个文件的长度

实现一个简单的co

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function co(fn) {
return function(done) {
const ctx = this;
const gen = fn.call(ctx);
let it = null;
function _next(err, res) {
if(err) res = err;
it = gen.next(res);
//{value:function(){},done:false}
if(!it.done){
it.value(_next);
}
}
_next();
}
}

这部分的核心代码为:

1
2
3
4
5
6
7
8
9
10
function _next(err, res) {
if(err) res = err;
it = gen.next(res);
//{value:function(){},done:false}
if(!it.done){
it.value(_next);
}
}

_next();

他的目的是递归调用generator的next(),直到done为true时,停止递归。 在调用第一次next()的之后,it为:

1
2
3
4
it={
value:(fn)=>{fs.readFile(file,'utf8',fn)},
done:false
}

it.donefalse的情况下,将_next()这个函数作为实参传到(fn)=>{fs.readFile(file,'utf8',fn)}中。 这时才正式执行读取文件的操作,res为读取的文件内容,之后再次执行_next()。 第二次调用_next()后,将res值塞回gen中,之后执行:

1
2
const b=res
console.log(b.length)

之后往后执行,遇到yield又中断了,再次读取c.js中的内容,之后又进入_next(),再把res值塞回gen中,和上次一样执行下列代码:

1
2
const c=res
console.log(c.length)

之后再触发_next()知道done状态为true

全部代码

这里的执行顺序比较乱,强烈建议大家去加断点多跑几遍!

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
30
31
32
function co(fn) {
return function(done) {
const ctx = this;
const gen = fn.call(ctx);
let it = null;
function _next(err, res) {
if(err) res = err;
it = gen.next(res);
//{value:function(){},done:false}
if(!it.done){
it.value(_next);
}
}
_next();
}
}

function read(file){
return (fn)=>{
fs.readFile(file,'utf8',fn)
}
}

const gen=function *(){
const b=yield read('error.js')
console.log(b.length)

const c=yield read('package.json')
console.log(c.length)
}

co(gen)()
  • 版权声明: 本博客所有文章,未经许可,任何单位及个人不得做营利性使用!转载请标明出处!如有侵权请联系作者。
  • Copyrights © 2015-2020 翟天野

请我喝杯咖啡吧~