资讯专栏INFORMATION COLUMN

Callback 与 Promise 间的桥梁 —— promisify

RayKr / 1545人阅读

摘要:今天要介绍的是,就是回调函数与间的桥梁。什么样的叫有两个条件回调函数在主函数中的参数位置必须是最后一个回调函数参数中的第一个参数必须是。

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

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

回调函数在主函数中的参数位置

// 正确
function main(a, b, c, callback) {
    
}

// 错误
function main(callback, a, b, c) {
    
}

回调函数参数中的第一个参数必须是 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/ev...

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/89433.html

相关文章

  • util.promisify 的那些事儿

    摘要:自定义的化有那么一些场景,是不能够直接使用来进行转换的,有大概这么两种情况没有遵循约定的回调函数返回多个参数的回调函数首先是第一个,如果没有遵循我们的约定,很可能导致的误判,得不到正确的反馈。 util.promisify是在node.js 8.x版本中新增的一个工具,用于将老式的Error first callback转换为Promise对象,让老项目改造变得更为轻松。 在官方推...

    shuibo 评论0 收藏0
  • Promisify 的源码解析

    摘要:参考文档升级后的函数回调参数问题中的使用方法和还是不一样的源码讲解的内部机制优化相关内容文章官方文档简述使用过的都知道这个方法的作用,通过该方法会让形式的函数风格转换成方法,可以认为是一颗语法糖,例如接下来我们就分析一下这个的内部流程。 参考文档 升级bluebird 3后Promise.promisify的函数回调参数问题:3中的使用方法和2还是不一样的 How does Bl...

    gougoujiang 评论0 收藏0
  • Promise化,Promisify,将函数改成 既支持Callback回调,又支持Promise

    摘要:初学,用开发项目。而且还是个不小的项目,说起来挺冒险的。一开始比较简单,并没有使用也能顺利进行,随着开发的深入,到了不用不行的地步了。于是产生了一个问题,一些之前写的方法并不支持,如果直接改成,那之前调用过的地方也都得改,工作量有点大。 初学 Node.js,用 Express 开发 Web 项目。而且还是个不小的项目,说起来挺冒险的。 一开始比较简单,并没有使用 Promise 也能...

    wushuiyong 评论0 收藏0
  • Promise遍历(循环,无穷多的then)遇到的问题及三个解决方案

    摘要:今天碰到一个需要用做无穷循环的一个案例,顿时脑洞大开。事情是这样的,有这样的一群异步函数,将它们封装成,依次放入一个数组内需要让数组里的每一个依次进行,最后一个执行完就结束。 今天碰到一个需要用Promise做无穷循环then的一个案例,顿时脑洞大开。事情是这样的,有这样的一群异步函数, var func1 = function(callback){ setTimeout(fu...

    freecode 评论0 收藏0
  • 适配器在JavaScript中的体现

    摘要:而适配器其实在中应该是比较常见的一种了。在维基百科中,关于适配器模式的定义为在软件工程中,适配器模式是一种软件设计模式,允许从另一个接口使用现有类的接口。 适配器设计模式在JavaScript中非常有用,在处理跨浏览器兼容问题、整合多个第三方SDK的调用,都可以看到它的身影。 其实在日常开发中,很多时候会不经意间写出符合某种设计模式的代码,毕竟设计模式就是老前辈们总结提炼出来的一些能...

    z2xy 评论0 收藏0

发表评论

0条评论

RayKr

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<