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

2024-05-11

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

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

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

};

2. 普通回调:

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

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

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

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

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

我如何在 Promise 中使用 API,如何“Promisify”它?


Promise 有状态,它们开始时处于待处理状态,并且可以解决:

  • 实现了意味着计算成功完成。
  • rejected意味着计算失败。

Promise 返回函数永远不应该扔 https://stackoverflow.com/q/21887856/1048572,他们应该返回拒绝。从承诺返回函数中抛出将迫使您同时使用} catch { and a .catch。使用 Promisified API 的人并不期望 Promise 会抛出异常。如果您不确定异步 API 在 JS 中如何工作 - 请看到这个答案 https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call/16825593#16825593 first.

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

因此,创建承诺通常意味着指定它们何时解决 - 这意味着它们何时进入履行或拒绝阶段以指示数据可用(并且可以通过.then).

通过现代的承诺实现来支持Promise像原生 ES6 承诺的构造函数:

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

然后,您可以像这样使用生成的 Promise:

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

使用支持延迟的库(我们在此示例中使用 $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();
}

2. 普通回调:

这些 API 相当常见,因为回调在 JS 中很常见。让我们看一下常见的情况onSuccess and onFail:

function getUserData(userId, onLoad, onFail) { …

通过现代的承诺实现来支持Promise像原生 ES6 承诺的构造函数:

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

使用支持延迟的库(我们在此示例中使用 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 延迟的事实resolve and reject方法是“可分离的”; IE。他们必须遵守instancejQuery.Deferred() 的。并非所有库都提供此功能。

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

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

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

使用延迟,您可以执行以下操作(本例中我们使用 Q,尽管 Q 现在支持新语法你应该更喜欢哪个 https://stackoverflow.com/q/28687566/1048572):

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,大多数以 Node 为设计理念的 Promise 库以及 Node 8+ 中的本机 Promise 都有一个用于 Promise NodeBacks 的内置方法。例如

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

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

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

Promise.promisifyAll(API);

Or with 原生承诺 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:

  • 当然,当你处于一个.then处理程序你不需要承诺的事情。返回来自 a 的承诺.then处理程序将使用该承诺的值来解决或拒绝。从一个投掷.thenhandler 也是很好的做法,会拒绝 Promise——这就是著名的 Promise 抛出安全性。
  • 在实际的onload情况下,你应该使用addEventListener而不是onX.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将现有回调 API 转换为 Promise? 的相关文章

随机推荐