我想使用 Promise,但我有一个回调 API,其格式如下:
1。 DOM 加载或其他一次性事件:
window.onload; // set to callback
...
window.onload = function() {
};
2。普通回调:
function request(onChangeHandler) {
...
}
request(function() {
// change happened
...
});
3。 Node 样式回调(“nodeback”):
function getStuff(dat, callback) {
...
}
getStuff("dataParam", function(err, data) {
...
})
4。具有 Node 样式回调的整个库:
API;
API.one(function(err, data) {
API.two(function(err, data2) {
API.three(function(err, data3) {
...
});
});
});
如何在 Promise 中使用 API,如何“Promisify”它?
最佳答案
Promise 有状态,它们开始时处于待处理状态,并且可以解决:
- fulfilled 表示计算成功完成。
- 拒绝表示计算失败。
Promise 返回函数 should never throw ,他们应该返回拒绝。从 promise 返回函数中抛出将迫使您同时使用 } catch {
和 .catch
。使用 Promisified API 的人并不期望 Promise 会抛出异常。如果您不确定异步 API 在 JS 中如何工作 - 请see this answer首先。
1。 DOM 加载或其他一次性事件:
因此,创建 Promise 通常意味着指定它们何时解决 - 这意味着它们何时进入已完成或拒绝阶段以指示数据可用(并且可以使用 .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 的 API, Hook 发生一次的事件:
function done() {
var d = $.Deferred();
$("#myObject").once("click",function() {
d.resolve();
});
return d.promise();
}
2。普通回调:
这些 API 相当常见,因为回调在 JS 中很常见。让我们看一下 onSuccess
和 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 的 resolve
和 reject
方法是“可分离的”这一事实; IE。它们绑定(bind)到 jQuery.Deferred() 的实例。并非所有库都提供此功能。
3。 Node 样式回调(“nodeback”):
Node 样式回调(nodebacks)具有特定的格式,其中回调始终是最后一个参数,而其第一个参数是错误。让我们首先手动 promise 一个:
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);
});
});
}
使用 deferreds,您可以执行以下操作(本例中我们使用 Q,尽管 Q 现在支持新语法 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;
}
一般来说,您不应该过多地手动进行 promise ,大多数以 Node 为设计理念的 promise 库以及 Node 8+ 中的 native promise 都有一个用于 promise Nodeback 的内置方法。例如
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only
4。具有 Node 样式回调的整个库:
这里没有黄金法则,你一一 promise 。但是,某些 Promise 实现允许您批量执行此操作,例如在 Bluebird 中,将 Nodeback API 转换为 Promise API 非常简单:
Promise.promisifyAll(API);
或者使用 Node 中的原生 Promise:
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
处理程序中时,您不需要 promise 任何事情。从.then
处理程序返回一个 Promise 将使用该 Promise 的值来解析或拒绝。从.then
处理程序抛出也是一种很好的做法,并且会拒绝 promise - 这就是著名的 promise 抛出安全性。 - 在实际的
onload
情况下,您应该使用addEventListener
而不是onX
。
关于javascript - 如何将现有回调 API 转换为 Promise?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39711717/