如何将现有的回调 API 转换为 Promise? [英] How do I convert an existing callback API to promises?

查看:33
本文介绍了如何将现有的回调 API 转换为 Promise?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

1.DOM 加载或其他一次性事件:

window.onload;//设置为回调...window.onload = 函数(){};

2.普通回调:

函数请求(onChangeHandler){...}请求(功能(){//发生了变化...});

3.节点样式回调(nodeback"):

function getStuff(dat, callback) {...}getStuff("dataParam", function(err, data) {...})

4.具有节点样式回调的整个库:

API;API.one(函数(错误,数据){API.two(function(err, data2) {API.三(函数(错误,数据3){...});});});

我如何使用 Promise 中的 API,我如何承诺"它?

解决方案

Promises 有状态,它们以待定状态开始,并且可以解决:

  • fulfilled 表示计算成功完成.
  • rejected 表示计算失败.

Promise 返回函数不应该抛出,它们应该返回拒绝.从承诺返回函数中抛出将迫使您同时使用 } catch { .catch.使用promisified API 的人不期望承诺会抛出.如果您不确定异步 API 如何在 JS 中工作 - 请首先查看此答案.

1.DOM 加载或其他一次性事件:

因此,创建 Promise 通常意味着指定它们何时解决 - 这意味着它们何时进入已完成或拒绝阶段以指示数据可用(并且可以使用 .then 访问).

使用支持 Promise 构造函数的现代 Promise 实现,如原生 ES6 承诺:

function load() {返回新的承诺(功能(解决,拒绝){window.onload = 解决;});}

然后你会像这样使用结果承诺:

load().then(function() {//onload 之后做的事情});

使用支持延迟的库(我们在此示例中使用 $q,但稍后我们也会使用 jQuery):

function load() {var d = $q.defer();window.onload = function() { d.resolve();};返回 d. 承诺;}

或者使用类似 jQuery 的 API,钩住一个发生一次的事件:

function done() {var d = $.Deferred();$("#myObject").once("点击",function() {d.解决();});返回 d.promise();}

2.普通回调:

这些 API 很常见,因为好吧……回调在 JS 中很常见.让我们看看有 onSuccessonFail 的常见情况:

function getUserData(userId, onLoad, onFail) { ...

使用支持 Promise 构造函数的现代 Promise 实现,如原生 ES6 承诺:

function getUserDataAsync(userId) {返回新的承诺(功能(解决,拒绝){getUserData(userId, 解决, 拒绝);});}

使用支持延迟的库(让我们在此示例中使用 jQuery,但我们在上面也使用了 $q):

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

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

function getUserDataAsync(userId) {返回 $.Deferred(function(dfrd) {getUserData(userId, dfrd.resolve, dfrd.reject);}).承诺();}

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

3.节点样式回调(nodeback"):

节点风格的回调(nodebacks)有一种特殊的格式,回调总是最后一个参数,它的第一个参数是一个错误.让我们首先手动承诺一个:

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

致:

function getStuffAsync(param) {返回新的承诺(功能(解决,拒绝){getStuff(参数,函数(错误,数据){如果(错误!== null)拒绝(错误);否则解决(数据);});});}

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

function getStuffAsync(param) {var d = Q.defer();getStuff(参数,函数(错误,数据){if (err !== null) d.reject(err);否则 d.resolve(data);});返回 d. 承诺;}

一般来说,你不应该过多地手动承诺事情,大多数为 Node 设计的承诺库以及 Node 8+ 中的原生承诺都有一个内置的方法来承诺节点背.例如

var getStuffAsync = Promise.promisify(getStuff);//蓝鸟var getStuffAsync = Q.denodeify(getStuff);//问var getStuffAsync = util.promisify(getStuff);//本机承诺,仅限节点

4.具有节点样式回调的整个库:

这里没有黄金法则,你一一承诺.但是,一些 promise 实现允许您批量执行此操作,例如在 Bluebird 中,将 nodeback API 转换为 promise API 非常简单:

Promise.promisifyAll(API);

或者在节点中使用原生承诺:

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}), {});

注意事项:

  • 当然,当您在 .then 处理程序中时,您不需要承诺事情.从 .then 处理程序返回承诺将使用该承诺的值解析或拒绝.从 .then 处理程序中抛出也是一种很好的做法,它会拒绝承诺 - 这就是著名的承诺抛出安全.
  • 在实际的 onload 情况下,您应该使用 addEventListener 而不是 onX.

I want to work with promises but I have a callback API in a format like:

1. DOM load or other one time event:

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

};

2. Plain callback:

function request(onChangeHandler) {
    ...
}
request(function() {
    // change happened
    ...
});

3. Node style callback ("nodeback"):

function getStuff(dat, callback) {
    ...
}
getStuff("dataParam", function(err, data) {
    ...
})

4. A whole library with node style callbacks:

API;
API.one(function(err, data) {
    API.two(function(err, data2) {
        API.three(function(err, data3) {
            ...
        });
    });
});

How do I work with the API in promises, how do I "promisify" it?

解决方案

Promises have state, they start as pending and can settle to:

  • fulfilled meaning that the computation completed successfully.
  • rejected meaning that the computation failed.

Promise returning functions should never throw, they should return rejections instead. Throwing from a promise returning function will force you to use both a } catch { and a .catch. People using promisified APIs do not expect promises to throw. If you're not sure how async APIs work in JS - please see this answer first.

1. DOM load or other one time event:

So, creating promises generally means specifying when they settle - that means when they move to the fulfilled or rejected phase to indicate the data is available (and can be accessed with .then).

With modern promise implementations that support the Promise constructor like native ES6 promises:

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

You would then use the resulting promise like so:

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

With libraries that support deferred (Let's use $q for this example here, but we'll also use jQuery later):

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

Or with a jQuery like API, hooking on an event happening once:

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

2. Plain callback:

These APIs are rather common since well… callbacks are common in JS. Let's look at the common case of having onSuccess and onFail:

function getUserData(userId, onLoad, onFail) { …

With modern promise implementations that support the Promise constructor like native ES6 promises:

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

With libraries that support deferred (Let's use jQuery for this example here, but we've also used $q above):

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

jQuery also offers a $.Deferred(fn) form, which has the advantage of allowing us to write an expression that emulates very closely the new Promise(fn) form, as follows:

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

Note: Here we exploit the fact that a jQuery deferred's resolve and reject methods are "detachable"; ie. they are bound to the instance of a jQuery.Deferred(). Not all libs offer this feature.

3. Node style callback ("nodeback"):

Node style callbacks (nodebacks) have a particular format where the callbacks is always the last argument and its first parameter is an error. Let's first promisify one manually:

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

To:

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

With deferreds you can do the following (let's use Q for this example, although Q now supports the new syntax which you should prefer):

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;   
}

In general, you should not promisify things manually too much, most promise libraries that were designed with Node in mind as well as native promises in Node 8+ have a built in method for promisifying nodebacks. For example

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

4. A whole library with node style callbacks:

There is no golden rule here, you promisify them one by one. However, some promise implementations allow you to do this in bulk, for example in Bluebird, converting a nodeback API to a promise API is as simple as:

Promise.promisifyAll(API);

Or with native promises in 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}), {});

Notes:

  • Of course, when you are in a .then handler you do not need to promisify things. Returning a promise from a .then handler will resolve or reject with that promise's value. Throwing from a .then handler is also good practice and will reject the promise - this is the famous promise throw safety.
  • In an actual onload case, you should use addEventListener rather than onX.

这篇关于如何将现有的回调 API 转换为 Promise?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆