美文网首页
Callback 与 Promise 间的桥梁 —— promi

Callback 与 Promise 间的桥梁 —— promi

作者: 我是灰灰的小跟班 | 来源:发表于2017-11-02 17:14 被阅读0次

    Callback与Promise间的桥梁-------promisify

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

    1.promisify 介绍

    什么是 promisify 呢?顾名思义,就是“promise 化”,将一个不是promise的方法变成 promise 。举个例子:

    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) {
    
    }
    
    2. 回调函数参数中的第一个参数必须是 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 ,在遇到这些方法的时候就可以更灵活地使用啦。

    相关文章

      网友评论

          本文标题:Callback 与 Promise 间的桥梁 —— promi

          本文链接:https://www.haomeiwen.com/subject/npfxnttx.html