我想使用承诺,但我有一个回调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的人并不期望允诺会抛出。 如果您不确定AsyncAPI在JS中是如何工作的--请先看看这个答案。
因此,创建承诺通常意味着指定它们何时结算--这意味着它们何时进入履行或拒绝阶段,以表明数据是可用的(并且可以使用.then
)。
使用支持promise
构造函数的现代promise实现(如本机ES6 promises):
function load() {
return new Promise(function(resolve, reject) {
window.onload = resolve;
});
}
然后,您将使用得到的承诺,如下所示:
load().then(function() {
// Do things after onload
});
使用支持deferred的库(这里的示例使用$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();
}
这些API很常见,因为回调在JS中很常见。 让我们看一下具有onsuccess
和onfail
的常见情况:
function getUserData(userId, onLoad, onFail) { …
使用支持promise
构造函数的现代promise实现(如本机ES6 promises):
function getUserDataAsync(userId) {
return new Promise(function(resolve, reject) {
getUserData(userId, resolve, reject);
});
}
使用支持deferred的库(这里的示例使用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 defered的resolve
和reject
方法是“可分离的”这一事实; 即,它们绑定到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);
});
});
}
使用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;
}
通常,您不应该过多地手动承诺,大多数考虑到节点而设计的promise库以及Node8+中的本机promise都有一个用于承诺NodeBacks的内置方法。 例如
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}), {});
注意事项:
.the
处理程序中时,您不需要承诺东西。 从返回一个承诺。然后
处理程序将用该承诺的值解析或拒绝该承诺。 从一个.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
。
好处:一个混合方法
(假设回调方法有两个参数作为错误和结果)
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 )
})
}
上述方法可以对旧的方式回调和承诺使用的结果进行响应。
希望这能帮上忙。
在将函数转换为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)
});