thunkify reposiroty 实现Generator 自动执行
thunkify 是自动执行 Generator 函数的一种方法,出自业界大神TJ。
- Javascript 的Thunk 函数
在 JavaScript 语言中,Thunk是将多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。
如下面的Demo,
const fs = require('fs');
const path = require('path');
const fpath = path.join(__dirname, 'a.txt');
var Thunk = function(fileName) {
return function(callBack) {
return fs.readFile(fileName, callBack);
}
}
var readFileThunk = Thunk(fpath);
readFileThunk(function(err, data) {
if (err) console.log(`${err}`);
else console.log(`${data}`);
})
上面的示例代码中自定义了Thunk 函数,
当var readFileThunk = Thunk(fpath)时,
其实代码返回的是函数
function(callBack) {
return fs.readFile(fileName, callBack);
}
这只是我自己实现的Thunk 函数,我们一起看看npm 第三方库的thunkify
- thunkify 库核心代码
function thunkify(fn) {
// thunkify 直接返回就是数组
return function() {
// 收集arguments 的参数,保存在临时数组中。
var args = new Array(arguments.length);
var ctx = this;
for (var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}
return function (done) {
var called;
// 将回调函数加载到上一级收集的参数数组的末尾。
args.push(function () {
// 确保回掉函数只会被加载一次。
if (called) return;
called = true;
done.apply(null, arguments);
});
try {
fn.apply(ctx, args);
} catch (err) {
done(err);
}
}
}
};
var fs = require('fs');
var read = thunkify(fs.readFile);
read('package.json', 'utf8')(function(err, str){
});
与我的Demo在原理上很相似,但是大神的代码严谨性更好,感觉被虐狗了。
- Thunk 函数有什么作用?
情景:如果我们想从一个文件中读取文件,然后在将读取的内容写入到另一个文件中。读文件是异步的,这时我们要阻塞线程,才能保证存入到新文件的内容是刚才读取的内容。
const fs = require('fs');
const thunkify = require('thunkify');
const path = require('path');
const fpath1 = path.join(__dirname, './test1.txt');
const fpath2 = path.join(__dirname, './test2.txt');
var readFileThunify = thunkify(fs.readFile);
var writeFileThunify = thunkify(fs.writeFile);
var gen = function *() {
var data = yield readFileThunify(fpath1);
yield writeFileThunify(fpath2, data);
}
var g = gen();
g.next().value((err, data) => {
if (err) console.log(`${err}`);
else {
console.log(`${data}`);
// 将读取到的内容传给写入文件的方法中
g.next(data).value((err, data) => {
if (err) console.log(`${err}`);
else console.log(`${data}`);
})
}
})
co repository - 基于Promise自动执行Generator对象
Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way.
官方介绍
- co 的特点
- 回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。
- Promise 对象。将异步操作包装成 Promise 对象,用then方法交回执行权。
以下是我通过thunk函数和Promise对象实现的简易generaor对象手动执行器
/**
* Generator 函数体
*/
const fs = require('fs');
const path = require('path');
var fpath1 = path.join(__dirname, './test1.txt');
var fpath2 = path.join(__dirname, './test2.txt');
function readFileFun(fpath) {
return new Promise(function(resolve, reject){
fs.readFile(fpath, function(err, result) {
if (err) reject(err);
else resolve(result);
});
});
}
var gen = function *() {
//yield 后是一个promise对象
var content1 = yield readFileFun(fpath1);
var content2 = yield readFileFun(fpath2);
console.log(`${content1}`);
console.log(`${content2}`);
}
/**
* manual perform generator.
*/
var g = gen();
g.next().value.then((data) => {
// 通过next() 函数向generator 函数体传值
g.next(data).value.then((data1) => {
g.next(data1);
})
})
下面是我写的简易自动化执行器
/**
* automatic perform generator.
*/
var preCO = require('./preCOGenerator');
function run(gen) {
var g = gen()
function next(data) {
var result = g.next(data);
// 如果generator 遍历结束,直接返回值。
if (result.done) return result.value;
// 通过then() 获取Promise 对象的返回值。
result.value.then(data => {
// 递归调用next()
next(data)
})
}
next();
}
run(preCO.gen);
- co 库核心代码
有兴趣点击co后自行查阅, 原理与我的简易版大同小异,但是菜鸟与大师的差异在于细节。