前言
随着Promise的广泛使用,我们习惯于将一般的使用回调的函数,转为返回Promise对象的函数,以供书写同步代码。
bluebird库提供了快速转换普通函数的方法:bluebird.Promise.promisify(),但是其内部设计用来支持回调函数第一个参数为err对象的标准范式,不适用于自定义的特别函数。
这里提供了一个优化的promisify(),可以通过额外参数支持特别类型的函数。
声明
promisify(fn, callbackErr, reverse)
- fn <function> 被包装的函数
- callbackErr <boolean> (default:true) fn的回调函数参数,其第一个参数是否为err对象
- reverse <boolean> (default:false) fn的回调函数参数,是否是第一个参数(如setTimeout)
源码
function promisify(fn, callbackErr=true, reverse=false) {
if ({}.toString.call(fn) !== '[object Function]') throw new TypeError('Only normal function can be promisified');
return function (...args) {
return new Promise((resolve, reject) => {
const callback = function (...args) {
if (!callbackErr) {
if (args.length === 1) return resolve(args[0]);
return resolve(args);
}
const err = args.shift();
const rest = args;
if ({}.toString.call(err) === '[object Error]') return reject(err);
if (rest.length === 1) return resolve(rest[0]);
return resolve(rest);
};
try {
if (reverse === true) fn.apply(null, [callback, ...args]);
else fn.apply(null, [...args, callback]);
} catch (err) {
reject(err);
}
});
}
}
使用
// 系统函数
const lstat = promisify(require('fs').lstat);
lstat('./index.js')
.then((stats) => {
console.log(stats)
})
.catch(err => console.warn('Catch error:', err));
// 自定义函数
function foo(a, b, cb) {
if(cb) cb(a+b);
}
const fooP = promisify(foo, false);
fooP(1, 2)
.then(res => console.log('res', res))
.catch(err => console.warn('Catch error:', err));
// setTimeout
const wait = promisify(setTimeout, true, true);
wait(1000).then(() => console.log('1秒后打印'));
// 方法,需要bind对象
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database(':memory:');
db.allP = promisify(db.all.bind(db));
db.allP('SELECT * FROM test').then(rows => console.log(rows));