我想使用 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 返回函数抛出将迫使您同时使用} catch {
和.catch
。使用承诺的 API 的人们不希望兑现承诺。如果您不确定 JS 中的异步 API 如何工作 - 请首先查看此答案。
因此,创建承诺通常意味着指定何时结算 - 即何时进入承诺阶段或拒绝阶段以指示数据可用(并且可以通过.then
进行访问)。
使用支持 Promise 构造函数的现代 Promise 实现(例如本机 ES6 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;
}
或使用 API 之类的 jQuery,挂接一次发生的事件:
function done() {
var d = $.Deferred();
$("#myObject").once("click",function() {
d.resolve();
});
return d.promise();
}
这些 API 相当常见,因为…… 在 JS 中回调很常见。让我们看一下具有onSuccess
和onFail
的常见情况:
function getUserData(userId, onLoad, onFail) { …
使用支持 Promise 构造函数的现代 Promise 实现(例如本机 ES6 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。它们绑定到 jQuery.Deferred()的实例。并非所有的库都提供此功能。
节点样式回调(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);
});
});
}
使用 deferreds,您可以执行以下操作(在本示例中,请使用 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}), {});
笔记:
.then
处理你不需要 promisify 事情。 .then
处理程序返回一个诺言将以该诺言的值进行解析或拒绝。从.then
处理程序中抛出也是很好的做法,并且会拒绝诺言 - 这就是著名的诺言抛出安全性。onload
情况下,应该使用addEventListener
而不是onX
。今天,我可以将Node.js
Promise
用作普通的 Javascript 方法。
一个简单而基本的Promise
示例(采用KISS方式):
普通的Javascript 异步 API 代码:
function divisionAPI (number, divider, successCallback, errorCallback) {
if (divider == 0) {
return errorCallback( new Error("Division by zero") )
}
successCallback( number / divider )
}
Promise
Javascript 异步 API 代码:
function divisionAPI (number, divider) {
return new Promise(function (fulfilled, rejected) {
if (divider == 0) {
return rejected( new Error("Division by zero") )
}
fulfilled( number / divider )
})
}
(我建议访问这个美丽的资源)
还Promise
ES7
async\await
一起使用,以使程序流等待fullfiled
结果,如下所示:
function getName () {
return new Promise(function (fulfilled, rejected) {
var name = "John Doe";
// wait 3000 milliseconds before calling fulfilled() method
setTimeout (
function() {
fulfilled( name )
},
3000
)
})
}
async function foo () {
var name = await getName(); // awaits for a fulfilled result!
console.log(name); // the console writes "John Doe" after 3000 milliseconds
}
foo() // calling the foo() method to run the code
.then()
方法使用同一代码的另一种用法
function getName () {
return new Promise(function (fulfilled, rejected) {
var name = "John Doe";
// wait 3000 milliseconds before calling fulfilled() method
setTimeout (
function() {
fulfilled( name )
},
3000
)
})
}
// the console writes "John Doe" after 3000 milliseconds
getName().then(function(name){ console.log(name) })
Promise
也可以在任何基于 Node.js 的平台上使用,例如react-native
。
奖励:一种混合方法
(假设回调方法具有两个参数,分别为 error 和 result)
function divisionAPI (number, divider, callback) {
return new Promise(function (fulfilled, rejected) {
if (divider == 0) {
let error = new Error("Division by zero")
callback && callback( error )
return rejected( error )
}
let result = number / divider
callback && callback( null, result )
fulfilled( result )
})
}
上面的方法可以响应老式回调和 Promise 用法的结果。
希望这可以帮助。
在 Node.JS 中将函数转换为 Promise 之前
var request = require('request'); //http wrapped module
function requestWrapper(url, callback) {
request.get(url, function (err, response) {
if (err) {
callback(err);
}else{
callback(null, response);
}
})
}
requestWrapper(url, function (err, response) {
console.log(err, response)
})
转换后
var request = require('request');
function requestWrapper(url) {
return new Promise(function (resolve, reject) { //returning promise
request.get(url, function (err, response) {
if (err) {
reject(err); //promise reject
}else{
resolve(response); //promise resolve
}
})
})
}
requestWrapper('http://localhost:8080/promise_request/1').then(function(response){
console.log(response) //resolve callback(success)
}).catch(function(error){
console.log(error) //reject callback(failure)
})
万一您需要处理多个请求
var allRequests = [];
allRequests.push(requestWrapper('http://localhost:8080/promise_request/1'))
allRequests.push(requestWrapper('http://localhost:8080/promise_request/2'))
allRequests.push(requestWrapper('http://localhost:8080/promise_request/5'))
Promise.all(allRequests).then(function (results) {
console.log(results);//result will be array which contains each promise response
}).catch(function (err) {
console.log(err)
});