作者:晃晃 本文原创,转载请注明作者及出处
Promise 自问世以来,得到了大量的应用,简直是 javascript 中的神器。它很好地解决了异步方法的回调地狱、提供了我们在异步方法中使用 return 的能力,并将 callback 的调用纳入了自己的管理,而不是交给异步函数后我们就无能为力了(经常有 callback 被莫名调用两次而导致程序出错)。
今天要介绍的是 Promisify,就是回调函数与 Promise 间的桥梁。
什么是 promisify 呢?顾名思义,就是“ promise 化”,将一个不是 promise 的方法变成 promise。举个例子:
// 原有的 callback 调用
fs.readFile('test.js', function(err, data) {
if (!err) {
console.log(data);
} else {
console.log(err);
}
});
// promisify 后
var readFileAsync = promisify(fs.readFile);
readFileAsync('test.js').then(data => {
console.log(data);
}, err => {
console.log(err);
});
这两个方法效果上是等价的,但是从掌控性来说的话,我更喜欢后面的写法。
那么什么样的方法可以通过 promisify 变成 promise 呢?这里就需要介绍一个名词,nodeCallback。什么样的 callback 叫 nodeCallback ?
nodeCallback 有两个条件:1. 回调函数在主函数中的参数位置必须是最后一个; 2. 回调函数参数中的第一个参数必须是 error。举个例子:
// 正确
function main(a, b, c, callback) {
}
// 错误
function main(callback, a, b, c) {
}
// 正确
function callback(error, result1, result2) {
}
// 错误
function callback(result1, result2, error) {
}
这样,通过 nodeCallback,我们定义了一个能被 promisify 的函数的格式,即,满足 nodeCallback 形式的方法,我们可以通过 promisify 来让它变成一个返回 promise 的方法。
下面我们来根据上述条件来手动实现一个 promisify。
首先 promisify 需要返回一个 function,并且这个 function 要返回一个 promise
var promisify = (func) => {
return function() {
var ctx = this;
return new Promise(resolve => {
return func.call(ctx, ...arguments);
})
}
}
其次,原 func 的最后一个参数是 callback
var promisify = (func) => {
return function() {
var ctx = this;
return new Promise(resolve => {
return func.call(ctx, ...arguments, function() {
resolve(arguments);
});
})
}
}
然后,回调函数中的第一个参数是 error 标记
var promisify = (func) => {
return function() {
var ctx = this;
return new Promise((resolve, reject) => {
return func.call(ctx, ...arguments, function() {
var args = Array.prototype.map.call(arguments, item => item);
var err = args.shift();
if (err) {
reject(err);
} else {
resolve(args);
}
});
})
}
}
最后,做一些优化,比如 this 作用域的自定义、回参只有一个时不返回数组
var promisify = (func, ctx) => {
// 返回一个新的 function
return function() {
// 初始化 this 作用域
var ctx = ctx || this;
// 新方法返回的 promise
return new Promise((resolve, reject) => {
// 调用原来的非 promise 方法 func,绑定作用域,传参,以及 callback ( callback 为 func 的最后一个参数)
func.call(ctx, ...arguments, function() {
// 将回调函数中的的第一个参数 error 单独取出
var args = Array.prototype.map.call(arguments, item => item);
var err = args.shift();
// 判断是否有 error
if (err) {
reject(err)
} else {
// 没有 error 则将后续参数 resolve 出来
args = args.length > 1 ? args : args[0];
resolve(args);
}
});
})
};
};
测试
// nodeCallback 方法 func1
var func1 = function(a, b, c, callback) {
callback(null, a+b+c);
}
// promise 化后的 func2
var func2 = promisify(func1);
// 调用后输出 6
func1(1, 2, 3, (err, reuslt) => {
if (!err) {
console.log(result); //输出 6
}
})
func2(1, 2, 3).then(console.log); //输出 6
以上便是 promisify 的介绍和实现,事实上有很多用 callback 来实现异步的第三方库提供的方法都是按照 nodeCallback 格式的,所以它们都可以通过 promisify 来让它变成 promise,在遇到这些方法的时候就可以更灵活地使用啦。
iKcamp 官网: http://www.ikcamp.com
访问官网更快阅读全部免费分享课程:《 iKcamp 出品|全网最新|微信小程序|基于最新版 1.0 开发者工具之初中级培训教程分享》。 包含:文章、视频、源代码
iKcamp 原创新书《移动 Web 前端高效开发实战》已在亚马逊、京东、当当开售。
与
“天天练口语”
小程序总榜排名第四、教育类排名第一的研发团队,面对面沟通交流。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.