Node.js本身的函数没有Promise化,如果要每个函数去封装,将Node.js的每个异步方法都转化成Promise,这将是比较大的代码量,第三方库Bluebird等提供了promisify的函数。 但是,promisify这类的函数到底是如何实现的呢?其实探究其根本,无非是利用Javascipt高阶函数的特征,利用偏函数等,构建一个promisify函数,来减少每次promisify一个api函数的代码量。 我们先来看看如何将Node.js的fs.readFile函数封装成Promise:
var promisifyFS = function(path, encode){
return new Promise(function(resolve, reject){
fs.readFile(path, encode, function(err, result){
if(err){
return reject(err);
}
return resolve(result);
});
});
};
promisifyFS('file1.txt', 'utf8').then(function(file1){
console.log(file1);
});
这是最简单的将fs.readFile包成Promise。 要将这个函数一般化,那我们必须将要promise化的方法当成一个参数传入,由于不同的函数接收的参数个数不一样,这样的话我们得用到method.apply()函数。
var promisify = function (method) {
return function (ctx) {
//获取method调用的需要参数
var args = Array.prototype.slice.call(arguments, 1);
//返回一个新的Promise对象
return new Promise(function (resolve, reject) {
//除了函数传入的参数以外还需要一个callback函数来供异步方法调用
var callback = function () {
return function (err, result) {
if (err) {
return reject(err);
}
return resolve(result);
};
}
args.push(callback());
//调用method
method.apply(ctx, args);
});
};
};
var readFile = promisify(fs.readFile);
readFile(null, 'file1.txt', 'utf8').then(function (file1) {
return readFile(null, file1.trim(), 'utf8');
}).then(function (file2) {
console.log(file2);
});
这样,一个简单的promisify函数就实现了。 代码见github:https://github.com/iyuq/promisify.git
参考文献:朴灵《深入浅出Node.js》
起码别把 context 丢了啊~
var promisefy = function (method, ctx) {
return function () {
//获取method调用的需要参数
var args = Array.prototype.slice.call(arguments, 0);
// use runtime this if ctx not provided
ctx = ctx || this;
//返回一个新的Promise对象
return new Promise(function (resolve, reject) {
//除了函数传入的参数以外还需要一个callback函数来供异步方法调用
var callback = function () {
return function (err, result) {
if (err) {
return reject(err);
}
return resolve(result);
};
}
args.push(callback());
//调用method
method.apply(ctx, args);
});
};
};
@magicdawn 谢谢提醒,已修改
promisify?
@reverland 好吧,我拼错了
promise 和 chaining pattern是什么关系?
@CarlosRen promise实现了chaining pattern
求问这个地方为什么要用一个函数返回一个函数包装一层。 为什么不能直接callback = function(err, result) {...}
, push的时候直接args.push(callback)
var callback = function () {
return function (err, result) {
if (err) {
return reject(err);
}
return resolve(result);
};
}