一尘不染

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

javascript

我想使用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​​,如何“承诺”呢?


阅读 693

收藏
2020-04-22

共1个答案

一尘不染

承诺具有状态,它们从待定状态开始,可以解决:

  • fulfilled成 意味着计算成功完成。
  • fulfilled 表示计算失败。

承诺返回函数绝不应该抛出
,而应该返回拒绝。从promise返回函数抛出将迫使你同时使用a } catch { a
.catch。使用承诺的API的人们不希望兑现承诺。如果你不确定JS中异步API的工作方式-

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

因此,创建承诺通常意味着指定何时结算-意味着何时进入完成或拒绝阶段以指示数据可用(并且可以通过访问.then)。

使用支持Promise本地构造函数(如本地ES6 Promise)的现代Promise实现:

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之类的jQuery,将发生的事件挂钩一次:

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

2.普通回调:

这些API相当常见,因为……在JS中回调很常见。让我们看看具有onSuccessand 的常见情况onFail

function getUserData(userId, onLoad, onFail) { …

使用支持Promise本地构造函数(如本地ES6 Promise)的现代Promise实现:

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的deferred resolvereject方法是“可分离的”
的事实。即。它们绑定到jQuery.Deferred()的 实例 。并非所有库都提供此功能。

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

节点样式回调(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;   
}

通常,你不应该过多地手动分配内容,大多数基于Node设计的Promise库以及Node
8+中的本机Promise具有内置的用于使NodeBback合理化的方法。例如

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

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

笔记:

  • 当然,当你在.then处理程序中时,你不需要散布事物。从.then处理程序返回一个承诺将解决或拒绝该承诺的值。从.then处理程序中扔出也是一种好习惯,并且会拒绝诺言-这就是著名的诺言抛出安全性。
  • 在实际onload情况下,你应该使用addEventListener而不是onX
2020-04-22