提问者:小点点

如何将现有的回调API转换为promise?


我想使用承诺,但我有一个回调API,格式如下:

window.onload; // set to callback
...
window.onload = function() {

};
function request(onChangeHandler) {
    ...
}
request(function() {
    // change happened
    ...
});
function getStuff(dat, callback) {
    ...
}
getStuff("dataParam", function(err, data) {
    ...
})
API;
API.one(function(err, data) {
    API.two(function(err, data2) {
        API.three(function(err, data3) {
            ...
        });
    });
});

共3个答案

匿名用户

承诺有状态,它们开始为待定,并可以确定为:

  • 已完成,表示计算已成功完成。
  • 拒绝,表示计算失败。

承诺返回函数不应该抛出,它们应该返回拒绝。 从promise返回函数抛出将强制您同时使用}catch{.catch。 使用允诺API的人并不期望允诺会抛出。 如果您不确定AsyncAPI在JS中是如何工作的--请先看看这个答案。

因此,创建承诺通常意味着指定它们何时结算--这意味着它们何时进入履行或拒绝阶段,以表明数据是可用的(并且可以使用.then)。

使用支持promise构造函数的现代promise实现(如本机ES6 promises):

function load() {
    return new Promise(function(resolve, reject) {
        window.onload = resolve;
    });
}

然后,您将使用得到的承诺,如下所示:

load().then(function() {
    // Do things after onload
});

使用支持deferred的库(这里的示例使用$Q,但稍后我们还将使用jQuery):

function load() {
    var d = $q.defer();
    window.onload = function() { d.resolve(); };
    return d.promise;
}

或者使用jQuery之类的API,挂接一次发生的事件:

function done() {
    var d = $.Deferred();
    $("#myObject").once("click",function() {
        d.resolve();
    });
    return d.promise();
}

这些API很常见,因为回调在JS中很常见。 让我们看一下具有onsuccessonfail的常见情况:

function getUserData(userId, onLoad, onFail) { …

使用支持promise构造函数的现代promise实现(如本机ES6 promises):

function getUserDataAsync(userId) {
    return new Promise(function(resolve, reject) {
        getUserData(userId, resolve, reject);
    });
}

使用支持deferred的库(这里的示例使用jQuery,但我们在上面也使用了$q):

function getUserDataAsync(userId) {
    var d = $.Deferred();
    getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
    return d.promise();
}

jQuery还提供了一个$.deferred(fn)表单,它的优点是允许我们编写一个非常接近New Promise(fn)表单的表达式,如下所示:

function getUserDataAsync(userId) {
    return $.Deferred(function(dfrd) {
        getUserData(userId, dfrd.resolve, dfrd.reject);
    }).promise();
}

注意:这里我们利用了jQuery defered的resolvereject方法是“可分离的”这一事实; 即,它们绑定到jQuery.deferred()的实例。 并非所有的库都提供此功能。

节点样式回调(nodebacks)有一种特殊的格式,其中回调始终是最后一个参数,而它的第一个参数是一个错误。 我们先手动许诺一个:

getStuff("dataParam", function(err, data) { …

致:

function getStuffAsync(param) {
    return new Promise(function(resolve, reject) {
        getStuff(param, function(err, data) {
            if (err !== null) reject(err);
            else resolve(data);
        });
    });
}

使用deferred,您可以执行以下操作(让我们在本例中使用Q,尽管Q现在支持您应该喜欢的新语法):

function getStuffAsync(param) {
    var d = Q.defer();
    getStuff(param, function(err, data) {
        if (err !== null) d.reject(err);
        else d.resolve(data);
    });
    return d.promise;   
}

通常,您不应该过多地手动承诺,大多数考虑到节点而设计的promise库以及Node8+中的本机promise都有一个用于承诺NodeBacks的内置方法。 例如

var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only

这里没有金科玉律,你一个一个地允诺他们。 但是,一些promise实现允许您批量完成此操作,例如在Bluebird中,将nodeback API转换为promise API就像:

Promise.promisifyAll(API);

或者在Node中使用本机承诺:

const { promisify } = require('util');
const promiseAPI = Object.entries(API).map(([key, v]) => ({key, fn: promisify(v)}))
                         .reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {});

注意事项:

  • 当然,当您在.the处理程序中时,您不需要承诺东西。 从返回一个承诺。然后处理程序将用该承诺的值解析或拒绝该承诺。 从一个.then处理程序中抛出也是很好的实践,会拒绝承诺--这就是著名的承诺抛出安全。
  • 在实际的onload情况下,应使用AddEventListener而不是onx

匿名用户

今天,我可以使用node.js中的promise作为普通Javascript方法。

promise的一个简单而基本的示例(使用KISS方式):

纯Javascript异步API代码:

function divisionAPI (number, divider, successCallback, errorCallback) {

    if (divider == 0) {
        return errorCallback( new Error("Division by zero") )
    }

    successCallback( number / divider )

}

promiseJavascript异步API代码:

function divisionAPI (number, divider) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            return rejected( new Error("Division by zero") )
        }

        fulfilled( number / divider )

     })

}

(我推荐访问这个美丽的来源)

此外,promise可以与ES7中的async\await一起使用,以使程序流等待fullfiled结果,如下所示:

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


async function foo () {

    var name = await getName(); // awaits for a fulfilled result!

    console.log(name); // the console writes "John Doe" after 3000 milliseconds

}


foo() // calling the foo() method to run the code

使用.then()方法对相同代码的另一种用法

function getName () {

    return new Promise(function (fulfilled, rejected) {

        var name = "John Doe";

        // wait 3000 milliseconds before calling fulfilled() method
        setTimeout ( 
            function() {
                fulfilled( name )
            }, 
            3000
        )

    })

}


// the console writes "John Doe" after 3000 milliseconds
getName().then(function(name){ console.log(name) })

promise也可以在任何基于node.js的平台上使用,比如react-native

好处:一个混合方法
(假设回调方法有两个参数作为错误和结果)

function divisionAPI (number, divider, callback) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            let error = new Error("Division by zero")
            callback && callback( error )
            return rejected( error )
        }

        let result = number / divider
        callback && callback( null, result )
        fulfilled( result )

     })

}

上述方法可以对旧的方式回调和承诺使用的结果进行响应。

希望这能帮上忙。

匿名用户

在将函数转换为Node.js中的promise之前

var request = require('request'); //http wrapped module

function requestWrapper(url, callback) {
    request.get(url, function (err, response) {
      if (err) {
        callback(err);
      }else{
        callback(null, response);             
      }      
    })
}


requestWrapper(url, function (err, response) {
    console.log(err, response)
})

转换后

var request = require('request');

function requestWrapper(url) {
  return new Promise(function (resolve, reject) { //returning promise
    request.get(url, function (err, response) {
      if (err) {
        reject(err); //promise reject
      }else{
        resolve(response); //promise resolve
      }
    })
  })
}


requestWrapper('http://localhost:8080/promise_request/1').then(function(response){
    console.log(response) //resolve callback(success)
}).catch(function(error){
    console.log(error) //reject callback(failure)
})

如果您需要处理多个请求

var allRequests = [];
allRequests.push(requestWrapper('http://localhost:8080/promise_request/1')) 
allRequests.push(requestWrapper('http://localhost:8080/promise_request/2'))
allRequests.push(requestWrapper('http://localhost:8080/promise_request/5'))    

Promise.all(allRequests).then(function (results) {
  console.log(results);//result will be array which contains each promise response
}).catch(function (err) {
  console.log(err)
});