/ javaScript

是时候把 Promise 写成一般的函数调用了

懒是一个程序员的好习惯,没错就是这样。

问题

假设使用 JQuery 来写一个 Promise

  1. 调用时需要传入参数,比如读取文件的文件名。
  2. 将生成的 Promise 的对象返回出去。

那么这个方法大概是这样的:

var getFile = function (fileName){
    var defer = $.Deferred();
    
    //一些异步的操作,比如读取一个文件
    readFile(fileName, function(file){
        defer.resolve(file);
    })
    
    return defer.promise();
}
getFile('a.txt').then(function(result){
    console.log(result);
})

感觉还是挺简洁干净的,但对于每天要接触 js 中的异步操作的我来说,这在我脑里就是这样:

while(true){
    var defer = $.Deferred();
    defer.resolve(result);
    return defer.promise();
}

能不能把这 3 行给干掉啊,这样我就能少些 n * 3 行代码了。我也不想显示的控制返回,因为需要我脑子参与的应该就仅仅是写个逻辑。

解决

让函数返回函数,内层函数声明 Promise 函数,并在最后返回该 Promise 对象。

需要执行的函数以参数的形式传入到外层函数,在内层函数中进行调用,将 Promise 修改状态的两个函数 resolvereject 以参数形式传入以参数形式传入的函数 func

感觉有点绕,直接上代码吧。

首先不考虑执行的函数需要传递传参的情况,这种情况比较简单。

function q(func){
    return function(){
        var defer = $.Deferred();
        func && func(defer.resolve, defer.reject);
        return defer.promise();
    }
}

然后 getFile 就变成这样了:

var getFile= q(function (resolve, reject){
    readFile('a.txt', function(file){
        defer.resolve(file);
    })
})
getFile().then(function(result){
    console.log(result)
})

// 其实眼尖的应该能看出了,这和原生实现 promise 差不了太多啊
var getFile = new Promise(function (resolve, reject){
    readFile('a.txt', function(file){
        defer.resolve(file);
    })
})
// 但是这是不允许传递参数的,就如例子中的文件名。

是不是有一种酣畅淋漓的感觉,光写主要的代码逻辑就好了,函数返回的就是 Promise

接下来考虑到 getFile 可能需要传入参数,就如同开头说的那样,而且我们要实现的是通用的函数,所以我们不知道实际传入的参数的个数,但是实现起来需要用到 applyarguments 这两个在 js 中不太被了解的东西。

思考后,撸出了这样的代码,供大家参考:

function q(func){
    return function(){
        var args = Array.prototype.slice.call(arguments);
        var defer = $.Deferred();
        args.push(defer.resolve, defer.reject);
        func && func.apply(undefined, args);
        return defer.promise();
    }
}

当然使用 ES6 语法就会更简单

function q(func){
    return () => {
        let defer = $.Deferred();
        func && func.call(undefined, ...arguments, defer.resolve, defer.reject);
        return defer.promise();
    }
}

那么 getFile 就可以这样了:

var getFile = q(function (fileName, resolve, reject){
    readFile(fileName, function(file){
        resolve(file);
    })
})
getFile('a.txt').then(function(result){
    console.log(result);
})

// ES6
// 本来不想放上 ES6 的实现的,但确实 ES6 写起来更加的舒服。

let getFile = q((fileName, resolve, reject) => {
    readFile(fileName, file => resolve(file))
})
getFile('a.txt').then(result => console.log(result))

对于其他的不同实现的 Promise 比如说 $qQ 、原生实现的 Promise 也是一样的。

以下就仅仅放出了 ES5 的写法了,毕竟是一个小小函数,用时临时写就行,这里就不过多的考虑了。

原生的 Promise

function q(func){
    return function(){
        var args = Array.prototype.slice.call(arguments);
        return new Promise(function(resolve, reject) {
            args.push(resolve, reject);
            func && func.apply(undefined, args);
        });
    }
}

angular 中的 $q

function q(func){
    return function(){
        var args = Array.prototype.slice.call(arguments);
        var defer = $q.defer();
        args.push(defer.resolve, defer.reject);
        func && func.apply(undefined, args);
        return defer.promise;
    }
}

无论多少个参数都没用问题,突然感觉如沐春风啊,不多说了我要把剩下来的时间拿去睡觉拿去 happy 了。

最后强调一遍:懒是一个程序员的好习惯,没错就是这样。