2.用同步的风格写异步逻辑及co库的实现原理
Zijue opened this issue · comments
用generator读取关联文件的内容
假设现在有两个文件a.txt
、b.txt
,内容如下:
.
├── a.txt # b.txt
└── b.txt # 紫珏
文件a
存放着文件b
的路径,文件b
中存放着最终要读取的内容;现在我们希望用generator来完成此逻辑:
let fs = require('fs').promises;
function* read() {
const a = yield fs.readFile('a.txt', 'utf8');
const b = yield fs.readFile(a, 'utf8');
return b;
}
最好就是向上面这段代码一样,像同步一样编写。变量a
等待fs.readFile('a.txt', 'utf8')
的结果,变量b
等待fs.readFile(a, 'utf8')
的结果并返回;但为了完成这样的功能,我们还需要编写调度器代码,很容易写出这样的代码:
let it = read();
let { value, done } = it.next();
value.then(data => {
let { value, done } = it.next(data);
value.then(data => {
let { value, done } = it.next(data);
console.log(value); // 紫珏
})
});
不难看出,这样的代码写起来特别麻烦,而且出错也不好捕获。于是就有大佬写了一个co
库用于解决这个问题,使用方法如下:
let fs = require('fs').promises;
function* read() { // 更像同步
const a = yield fs.readFile('a.txt', 'utf8');
const b = yield fs.readFile(a, 'utf8');
return b;
}
const co = require('co');
co(read()).then(data => {
console.log(data); // 紫珏
})
这样一来,代码就变得是否优雅与简洁。那么co
库是如何实现的呢?能调用then
方法,那么返回的一定是一个promise:
function co(it){
return new Promise((resolve, reject)=>{
// todo
})
}
其次就是为了串行执行读取文件的逻辑,需要异步迭代。我们在同步代码中迭代用for
,异步迭代则用递归;
function co(it) {
return new Promise((resolve, reject) => {
function next(data) {
let { value, done } = it.next(data);
if (done) {
return resolve(value);
}
Promise.resolve(value).then(data => {
next(data);
}, reject)
}
next();
})
}
koa
、express
都是基于这个co
写法的。