本文翻译自:How do I convert an existing callback API to promises?
I want to work with promises but I have a callback API in a format like: 我想使用Promise,但是我有一个类似以下格式的回调API:
1. DOM load or other one time event: 1. DOM加载或其他一次事件:
window.onload; // set to callback
...
window.onload = function() {
};
2. Plain callback: 2.普通回调:
function request(onChangeHandler) {
...
}
request(function() {
// change happened
...
});
3. Node style callback ("nodeback"): 3.节点样式回调(“ nodeback”):
function getStuff(dat, callback) {
...
}
getStuff("dataParam", function(err, data) {
...
})
4. A whole library with node style callbacks: 4.带有节点样式回调的整个库:
API;
API.one(function(err, data) {
API.two(function(err, data2) {
API.three(function(err, data3) {
...
});
});
});
How do I work with the API in promises, how do I "promisify" it? 如何在promise中使用API,如何“承诺”它?
#1楼
参考:https://stackoom.com/question/1WUQK/如何将现有的回调API转换为Promise
#2楼
Promises have state, they start as pending and can settle to: 承诺有状态,它们从待定状态开始,可以解决:
- fulfilled meaning that the computation completed successfully. 完成意味着计算成功完成。
- rejected meaning that the computation failed. 拒绝表示计算失败。
Promise returning functions should never throw , they should return rejections instead. 承诺返回函数绝不应该抛出 ,而应该返回拒绝。 Throwing from a promise returning function will force you to use both a } catch {
and a .catch
. 从promise返回函数抛出将迫使您同时使用} catch {
和 .catch
。 People using promisified APIs do not expect promises to throw. 使用承诺的API的人们不会期望诺言。 If you're not sure how async APIs work in JS - please see this answer first. 如果您不确定JS中异步API的工作方式-请首先查看此答案 。
1. DOM load or other one time event: 1. DOM加载或其他一次事件:
So, creating promises generally means specifying when they settle - that means when they move to the fulfilled or rejected phase to indicate the data is available (and can be accessed with .then
). 因此,创建承诺通常意味着指定何时结算-即何时进入承诺阶段或拒绝阶段以指示数据可用(并且可以通过.then
访问)。
With modern promise implementations that support the Promise
constructor like native ES6 promises: 使用支持Promise
构造函数的现代Promise
实现(例如本机ES6 Promise
:
function load() {
return new Promise(function(resolve, reject) {
window.onload = resolve;
});
}
You would then use the resulting promise like so: 然后,您将使用产生的承诺,如下所示:
load().then(function() {
// Do things after onload
});
With libraries that support deferred (Let's use $q for this example here, but we'll also use jQuery later): 使用支持延迟的库(让我们在此示例中使用$ q,但稍后我们还将使用jQuery):
function load() {
var d = $q.defer();
window.onload = function() { d.resolve(); };
return d.promise;
}
Or with a jQuery like API, hooking on an event happening once: 或者使用jQuery之类的jQuery,将一次事件挂起:
function done() {
var d = $.Deferred();
$("#myObject").once("click",function() {
d.resolve();
});
return d.promise();
}
2. Plain callback: 2.普通回调:
These APIs are rather common since well… callbacks are common in JS. 这些API相当常见,因为……在JS中回调很常见。 Let's look at the common case of having onSuccess
and onFail
: 让我们看一下具有onSuccess
和onFail
的常见情况:
function getUserData(userId, onLoad, onFail) { …
With modern promise implementations that support the Promise
constructor like native ES6 promises: 使用支持Promise
构造函数的现代Promise
实现(例如本机ES6 Promise
:
function getUserDataAsync(userId) {
return new Promise(function(resolve, reject) {
getUserData(userId, resolve, reject);
});
}
With libraries that support deferred (Let's use jQuery for this example here, but we've also used $q above): 使用支持延迟的库(在此示例中,我们使用jQuery,但上面我们也使用$ q):
function getUserDataAsync(userId) {
var d = $.Deferred();
getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
return d.promise();
}
jQuery also offers a $.Deferred(fn)
form, which has the advantage of allowing us to write an expression that emulates very closely the new Promise(fn)
form, as follows: jQuery还提供了$.Deferred(fn)
形式,它的优点是允许我们编写一个非常接近new Promise(fn)
形式的表达式,如下所示:
function getUserDataAsync(userId) {
return $.Deferred(function(dfrd) {
getUserData(userId, dfrd.resolve, dfrd.reject);
}).promise();
}
Note: Here we exploit the fact that a jQuery deferred's resolve
and reject
methods are "detachable"; 注意:这里我们利用了一个事实,即jQuery deferred的resolve
和reject
方法是“可分离的”。 ie. 即。 they are bound to the instance of a jQuery.Deferred(). 它们绑定到jQuery.Deferred()的实例 。 Not all libs offer this feature. 并非所有库都提供此功能。
3. Node style callback ("nodeback"): 3.节点样式回调(“ nodeback”):
Node style callbacks (nodebacks) have a particular format where the callbacks is always the last argument and its first parameter is an error. 节点样式回调(nodebacks)具有特定的格式,其中回调始终是最后一个参数,而其第一个参数是错误。 Let's first promisify one manually: 让我们首先手动分配一个:
getStuff("dataParam", function(err, data) { …
To: 至:
function getStuffAsync(param) {
return new Promise(function(resolve, reject) {
getStuff(param, function(err, data) {
if (err !== null) reject(err);
else resolve(data);
});
});
}
With deferreds you can do the following (let's use Q for this example, although Q now supports the new syntax which you should prefer ): 使用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;
}
In general, you should not promisify things manually too much, most promise libraries that were designed with Node in mind as well as native promises in Node 8+ have a built in method for promisifying nodebacks. 通常,您不应该过多地手动分配内容,大多数基于Node设计的Promise库以及Node 8+中的本机Promise具有内置的用于使NodeBback富集的方法。 For example 例如
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only
4. A whole library with node style callbacks: 4.带有节点样式回调的整个库:
There is no golden rule here, you promisify them one by one. 这里没有黄金法则,您一一承诺。 However, some promise implementations allow you to do this in bulk, for example in Bluebird, converting a nodeback API to a promise API is as simple as: 但是,某些promise实现允许您批量执行此操作,例如在Bluebird中,将nodeback API转换为promise API很简单:
Promise.promisifyAll(API);
Or with native promises in Node : 或在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}), {});
Notes: 笔记:
- Of course, when you are in a
.then
handler you do not need to promisify things. 当然,当你在一个.then
处理你不需要promisify事情。 Returning a promise from a.then
handler will resolve or reject with that promise's value. 从.then
处理程序返回一个诺言将解决或拒绝该诺言的值。 Throwing from a.then
handler is also good practice and will reject the promise - this is the famous promise throw safety. 从.then
处理程序中抛出也是很好的做法,并且会拒绝诺言-这就是著名的诺言抛出安全性。 - In an actual
onload
case, you should useaddEventListener
rather thanonX
. 在实际的onload
情况下,应该使用addEventListener
而不是onX
。
#3楼
I don't think the window.onload
suggestion by @Benjamin will work all the time, as it doesn't detect whether it is called after the load. 我认为@Benjamin的window.onload
建议不会一直有效,因为它无法检测到加载后是否被调用。 I have been bitten by that many times. 我被很多次咬伤了。 Here is a version which should always work: 这是一个应该始终有效的版本:
function promiseDOMready() {
return new Promise(function(resolve) {
if (document.readyState === "complete") return resolve();
document.addEventListener("DOMContentLoaded", resolve);
});
}
promiseDOMready().then(initOnLoad);
#4楼
The Q library by kriskowal includes callback-to-promise functions. kriskowal的Q库包含应答回调函数。 A method like this: 这样的方法:
obj.prototype.dosomething(params, cb) {
...blah blah...
cb(error, results);
}
can be converted with Q.ninvoke 可以用Q.ninvoke转换
Q.ninvoke(obj,"dosomething",params).
then(function(results) {
});
#5楼
You can use JavaScript native promises with Node JS. 您可以将JavaScript本机Promise与Node JS一起使用。
My Cloud 9 code link: https://ide.c9.io/adx2803/native-promises-in-node 我的Cloud 9代码链接: https : //ide.c9.io/adx2803/native-promises-in-node
/**
* Created by dixit-lab on 20/6/16.
*/
var express = require('express');
var request = require('request'); //Simplified HTTP request client.
var app = express();
function promisify(url) {
return new Promise(function (resolve, reject) {
request.get(url, function (error, response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
}
else {
reject(error);
}
})
});
}
//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
console.log(e);
})
.then(function (result) {
res.end(result);
})
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
//run webservice on browser : http://localhost:8081/listAlbums
#6楼
When you have a few functions that take a callback and you want them to return a promise instead you can use this function to do the conversion. 当您有一些需要回调的函数并且希望它们返回一个Promise时,您可以使用此函数进行转换。
function callbackToPromise(func){
return function(){
// change this to use what ever promise lib you are using
// In this case i'm using angular $q that I exposed on a util module
var defered = util.$q.defer();
var cb = (val) => {
defered.resolve(val);
}
var args = Array.prototype.slice.call(arguments);
args.push(cb);
func.apply(this, args);
return defered.promise;
}
}