虽然现在异步解决方案都用async/await
了,但co是一个小巧而精致的库,我们也可以学习一下他的实现原理。
本文首发于本人的github,欢迎star与watch~~OVO
什么是co
co是一个函数库,用于generator的自动执行。
我们先定义一个generator,用来异步读下面几个文件
const gen=function *(){
const b=yield read('b.js')
console.log(b.length)
const c=yield read('c.js')
console.log(c.length)
}
其中,我们定义的read异步读取文件的函数为:
function read(file){
fs.readFile(file,'utf8',(err,res)=>{
g.next(res)
})
}
const g=gen()
g.next() //将分别输出两个文件的长度
co的使用
虽然上面的代码也可以顺利异步执行generator函数,但是需要把next()
写到异步函数中,
增加了耦合度,而且还要创建generator的实例,代码很繁琐。
我们使用co重构上面的代码:
首先要改造一下异步函数的结构,co规定所有的异步函数必须是偏函数,称之为thunk函数:
function read(file){
return (fn)=>{
fs.readFile(file,'utf8',fn)
}
}
之后,只要把generator传到co中就大功告成了
const co=require('co')
co(gen)() //分别输出两个文件的长度
实现一个简单的co
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 _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
为:
it={
value:(fn)=>{fs.readFile(file,'utf8',fn)},
done:false
}
在it.done
为false
的情况下,将_next()
这个函数作为实参传到(fn)=>{fs.readFile(file,'utf8',fn)}
中。
这时才正式执行读取文件的操作,res为读取的文件内容,之后再次执行_next()
。
第二次调用_next()
后,将res
值塞回gen
中,之后执行:
const b=res
console.log(b.length)
之后往后执行,遇到yield
又中断了,再次读取c.js
中的内容,之后又进入_next()
,再把res
值塞回gen
中,和上次一样执行下列代码:
const c=res
console.log(c.length)
之后再触发_next()
知道done
状态为true
。
全部代码
这里的执行顺序比较乱,强烈建议大家去加断点多跑几遍!
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)()
相关链接
- co 函数库的含义和用法
- Generator 函数的含义与用法