Zijue / blog

personal knowledge collection

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2.用同步的风格写异步逻辑及co库的实现原理

Zijue opened this issue · comments

commented

用generator读取关联文件的内容

假设现在有两个文件a.txtb.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();
    })
}

koaexpress都是基于这个co写法的。