做者:晃晃
本文原创,转载请注明做者及出处javascript
Promise 自问世以来,获得了大量的应用,简直是 javascript 中的神器。它很好地解决了异步方法的回调地狱、提供了咱们在异步方法中使用 return 的能力,并将 callback 的调用归入了本身的管理,而不是交给异步函数后咱们就无能为力了(常常有 callback 被莫名调用两次而致使程序出错)。前端
今天要介绍的是 Promisify,就是回调函数与 Promise 间的桥梁。java
什么是 promisify 呢?顾名思义,就是“promise 化”,将一个不是promise的方法变成 promise 。举个例子:node
// 原有的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 的方法。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前端高效开发实战》已在亚马逊、京东、当当开售。
与
“每天练口语”
小程序总榜排名第4、教育类排名第一的研发团队,面对面沟通交流。