我想保证解决bluebird.js承诺的最小延迟。
作为一个例子,假设我正在将一个请求包含在一个承诺中。我想要的行为是,如果请求少于5秒,我想人为地将承诺解析的延迟增加到5秒。如果请求超过5秒,我不希望添加任何人工延迟 - 所以它比为每个请求添加静态延迟要复杂一些。所有这一切都应该完全隐藏在承诺的消费者身上 - 他们应该看到承诺在5秒或更长时间内得到解决。
为了演示,我有一个简单的模拟实现示例,它将模拟的请求延迟硬编码为3秒。
我的第一次尝试是这样的 - 使用setTimeout确保在5秒之前没有调用解析回调。
function getTimestamp() {
return new Date().getTime();
}
function makeCallWith3SecondLatency(cb) {
console.log('mocking a call with 3 second latency...');
var mockResult = 'the result';
setTimeout(function() { cb(mockResult); }, 3000);
}
function doSomethingAsync(minDelay) {
return new Promise(function(resolve) {
var calledAt = getTimestamp();
makeCallWith3SecondLatency(function(arg) {
var actualDelay = getTimestamp() - calledAt;
if(actualDelay < minDelay) {
var artificialDelay = minDelay - actualDelay;
console.log('artificially delay another ' + artificialDelay + ' millis');
setTimeout(function() { resolve(arg); }, artificialDelay);
} else {
resolve(arg);
}
});
});
}
function printResult(result) {
console.log('result: ' + result)
}
var minDelay = 5000;
doSomethingAsync(minDelay).then(printResult);
很多样板。
然后我通过this answer发现我可以使用Promise.join函数来加入包含请求的承诺,并使用最少5秒延迟的Promise.delay来实现同样的目的:
function makeCallWith3SecondLatency(cb) {
console.log('mocking a call with 3 second latency...');
var mockResult = 'the result';
setTimeout(function() { cb(mockResult); }, 3000);
}
function doSomethingAsync(minDelay) {
return Promise.join(
new Promise(function(resolve) { makeCallWith3SecondLatency(resolve); }),
Promise.delay(minDelay).then(function() { console.log('artificially delaying 5 seconds with Promise.delay') }),
function(result) { return result; });
}
function printResult(result) {
console.log('result: ' + result)
}
var minDelay = 5000;
doSomethingAsync(minDelay).then(printResult);
这个更干净,但仍然比我想要的更多样板 - 我在bluebird api reference周围挖了一个并且找不到直接执行此操作的函数。
我的问题很简单 - 任何人都可以建议使用蓝鸟比第二个例子更清晰,更具说服力的方式来实现这种行为吗?
api提供此功能的其他承诺库的任何建议也将受到赞赏。
答案 0 :(得分:6)
我相信您需要做的就是Promise.delay(value).return(promise)
:
您可以将其包装在效用函数中:
function stallPromise(promise, delay) {
return Promise.delay(delay).return(promise);
}
function doSomethingAsync(minDelay) {
var p = new Promise(makeCallWith3SecondLatency);
return stallPromise(p, minDelay);
}
var minDelay = 5000;
doSomethingAsync(minDelay).then(printResult);
http://jsfiddle.net/s572rg7y/1/
注意关于这一点的一点是,如果承诺拒绝,延迟的承诺将在五秒钟过去之前拒绝。这可能是理想的行为(正如@Benjamin Gruenbaum在评论中指出的那样),但如果您希望它立即拒绝,还有两个选择:
使用Promise.join
:
function stallPromise(promise, delay) {
// if you're using underscore/lodash, you can use _.identity for this
function identity(val) { return val; }
return Promise.join(promise, Promise.delay(delay), identity);
}
或@Benjamin Gruenbaum与Promise.all
的方法:
function minDelay(promise, delay) {
Promise.all([promise, Promise.delay(delay)]).get(0);
}
答案 1 :(得分:4)
首先,其他3秒电话的宣传在这里是无关紧要的,它不应该是承诺的一部分。虽然我很受宠若惊,但我喜欢.join
的回答,但这也不是我实际使用的工具。
首先,API调用只是一个任意的promise返回函数。
function yourApiCall(){
// your function that returns a promise AFTER promisificatin
}
实际上,我们并不关心它。它可能只是:
var p = ... ; //p is a promise
现在我们要确保在解决p。
之前至少经过3秒function minDelay(p, ms){ // stealing name from JLRishe's answer
return Promise.all([p, Promise.delay(ms)]).get(0);
}
采用任意承诺并返回至少ms
毫秒才能解析的承诺。
minDelay(p, 300).then(function(el){
// el is minDelay's return value and at least 300 ms have passed
});
你也可以把它放在Bluebird的原型上(如果你正在写一个库,一定要先得到你自己的孤立副本):
Promise.prototype.minDelay = function minDelay(ms){
// note that unlike the example above this will delay
// on rejections too
return Promise.delay(ms).return(this);
}
这会让你以声明的方式做到:
p.minDelay(300).then(function(res){
// access result
});
通常,当人们询问这个问题时,他们真正关心的是使函数最多每毫秒返回一个结果,或者使函数充当监视器以确定调用的频率。这是为了限制对速率限制的Web服务进行的调用次数。这应该限制在返回promise 的函数的级别。例如:
var queue = Promise.resolve();
function throttle(fn, ms){
var res = queue.then(function(){ // wait for queue
return fn(); // call the function
});
queue = Promise.delay(ms).return(queue); // make the queue wait
return res; // return the result
}
这可以让你这样做:
function myApiCall(){
// returns a promise
}
var api = throttle(myApiCall, 300); // make call at most every 300 ms;
api(); // calls will be sequenced and queued
api(); // calls will be made at most every 300 ms
api(); // just be sure to call this directly, return this to consumers
答案 2 :(得分:-1)
库spex专门用于处理使用promises时数据限制和负载平衡等问题。
在您的情况下,我们可以使用以下示例:
var spex = require('spex')(Promise);
function source(index, data, delay) {
var start = Date.now();
return new Promise(function (resolve) {
// request your data here;
var end = Date.now();
if (end - start < 5000) {
setTimeout(function () {
resolve();
}, 5000 - end + start);
} else {
resolve();
}
});
}
function dest(index, data, delay) {
// you can do additional load balancing here,
// while processing the data;
}
spex.sequence(source, dest)
.then(function (data) {
console.log("DATA:", data);
});
但这只是表面上的问题,因为该库可以让您实现更灵活,更先进(如果您需要)的策略来处理承诺请求。
对于您的情况,可能有趣的是参数delay
,它们被传递到源函数和目标函数中,因此负载平衡可以在需要时以双向方式处理。
此外,您可以使用具有相同负载平衡策略的方法page,但在页面中处理请求。