Callback 与 Promise 间的桥梁 promisify - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iKcamp
V2EX    前端开发

Callback 与 Promise 间的桥梁 promisify

  •  
  •   iKcamp
    ikcamp 2017-11-01 18:18:19 +08:00 2107 次点击
    这是一个创建于 2912 天前的主题,其中的信息可能已经有所发展或是发生改变。

    作者:晃晃 本文原创,转载请注明作者及出处

    Promise 自问世以来,得到了大量的应用,简直是 Javascript 中的神器。它很好地解决了异步方法的回调地狱、提供了我们在异步方法中使用 return 的能力,并将 callback 的调用纳入了自己的管理,而不是交给异步函数后我们就无能为力了(经常有 callback 被莫名调用两次而导致程序出错)。

    今天要介绍的是 Promisify,就是回调函数与 Promise 间的桥梁。

    1. promisify 介绍

    什么是 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。举个例子:

    1. 回调函数在主函数中的参数位置
    // 正确 function main(a, b, c, callback) { } // 错误 function main(callback, a, b, c) { } 
    1. 回调函数参数中的第一个参数必须是 error
    // 正确 function callback(error, result1, result2) { } // 错误 function callback(result1, result2, error) { } 

    这样,通过 nodeCallback,我们定义了一个能被 promisify 的函数的格式,即,满足 nodeCallback 形式的方法,我们可以通过 promisify 来让它变成一个返回 promise 的方法。

    2. promisify 的实现

    下面我们来根据上述条件来手动实现一个 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 前端高效开发实战》已在亚马逊、京东、当当开售。

    [ 11 月 11 号] 上海 iKcamp 最新活动

    报名地址: http://www.huodongxing.com/event/5409924174200

    “天天练口语”小程序总榜排名第四、教育类排名第一的研发团队,面对面沟通交流。

    1 条回复    2017-11-01 19:01:53 +08:00
    yoa1q7y
        1
    yoa1q7y  
       2017-11-01 19:01:53 +08:00
    ...
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5223 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 07:14 PVG 15:14 LAX 00:14 JFK 03:14
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86