我想使用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) { ... }); }); });
承诺具有状态,它们从待定状态开始,可以解决:
承诺返回函数绝不应该抛出 ,而应该返回拒绝。从promise返回函数抛出将迫使你同时使用a } catch { 和 a .catch。使用承诺的API的人们不希望兑现承诺。如果你不确定JS中异步API的工作方式-
} catch {
.catch
因此,创建承诺通常意味着指定何时结算-意味着何时进入完成或拒绝阶段以指示数据可用(并且可以通过访问.then)。
.then
使用支持Promise本地构造函数(如本地ES6 Promise)的现代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(); }
这些API相当常见,因为……在JS中回调很常见。让我们看看具有onSuccessand 的常见情况onFail:
onSuccess
onFail
function getUserData(userId, onLoad, onFail) { …
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)类似于该表单的表达式,如下所示:
$.Deferred(fn)
new Promise(fn)
function getUserDataAsync(userId) { return $.Deferred(function(dfrd) { getUserData(userId, dfrd.resolve, dfrd.reject); }).promise(); }
注意:这里我们利用了jQuery的deferred resolve和reject方法是“可分离的” 的事实。即。它们绑定到jQuery.Deferred()的 实例 。并非所有库都提供此功能。
resolve
reject
节点样式回调(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
这里没有黄金法则,你一一承诺。但是,某些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}), {});
笔记:
onload
addEventListener
onX