为 bluebird.js 承诺解决设置最小延迟
Set minimum delay on bluebird.js promise resolution
我想保证 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 函数加入以 Promise.delay 最少 5 秒延迟包装请求的承诺,以实现相同的目的:
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 周围挖掘,但找不到直接执行此操作的函数。
我的问题很简单 - 有谁能提出比第二个示例更清晰、更明确的方式来使用 bluebird 实现此行为?
api 提供此功能的其他 promise 库的任何建议也将不胜感激。
我相信你只需要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);
}
你的问题
首先,其他3秒调用的promisification在这里无关紧要,不应该是promise的一部分。虽然我很荣幸您喜欢我用 .join
的回答,但它也不是我在这里实际使用的工具。
首先,API 调用只是一个任意承诺 returning 函数。
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);
}
这需要一个任意的承诺,return 是一个至少需要 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
});
一个更普遍的问题
通常当人们问起这个问题时,他们真正关心的是让一个函数return最多每隔几毫秒产生一个结果,或者让一个函数充当监视器 调用的频率。这是为了限制对限速 Web 服务的调用次数。这应该限制在 return 承诺 的函数的 级别。例如:
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
库 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);
});
但这只是冰山一角,因为该库允许您实施更灵活、更高级(如果需要)的策略来处理 promise 请求。
对于您的情况,可能有趣的是参数 delay
已传递到源函数和目标函数,因此可以在需要时以两种方式处理负载平衡。
此外,您可以使用具有相同负载平衡策略的方法page,但在页面中处理请求。
这是一个最小延迟 Promise 的代码示例:
const sleep = async ms => new Promise(resolve => setTimeout(resolve, ms));
const pMinDelay = async (promise, ms) => {
const [result] = await Promise.all([promise, sleep(ms)]);
return result;
};
export default pMinDelay;
在您想要延迟 Promise 的地方导入 pMinDelay
函数。
希望对您有所帮助 ;)
我想保证 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 函数加入以 Promise.delay 最少 5 秒延迟包装请求的承诺,以实现相同的目的:
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 周围挖掘,但找不到直接执行此操作的函数。
我的问题很简单 - 有谁能提出比第二个示例更清晰、更明确的方式来使用 bluebird 实现此行为?
api 提供此功能的其他 promise 库的任何建议也将不胜感激。
我相信你只需要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);
}
你的问题
首先,其他3秒调用的promisification在这里无关紧要,不应该是promise的一部分。虽然我很荣幸您喜欢我用 .join
的回答,但它也不是我在这里实际使用的工具。
首先,API 调用只是一个任意承诺 returning 函数。
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);
}
这需要一个任意的承诺,return 是一个至少需要 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
});
一个更普遍的问题
通常当人们问起这个问题时,他们真正关心的是让一个函数return最多每隔几毫秒产生一个结果,或者让一个函数充当监视器 调用的频率。这是为了限制对限速 Web 服务的调用次数。这应该限制在 return 承诺 的函数的 级别。例如:
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
库 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);
});
但这只是冰山一角,因为该库允许您实施更灵活、更高级(如果需要)的策略来处理 promise 请求。
对于您的情况,可能有趣的是参数 delay
已传递到源函数和目标函数,因此可以在需要时以两种方式处理负载平衡。
此外,您可以使用具有相同负载平衡策略的方法page,但在页面中处理请求。
这是一个最小延迟 Promise 的代码示例:
const sleep = async ms => new Promise(resolve => setTimeout(resolve, ms));
const pMinDelay = async (promise, ms) => {
const [result] = await Promise.all([promise, sleep(ms)]);
return result;
};
export default pMinDelay;
在您想要延迟 Promise 的地方导入 pMinDelay
函数。
希望对您有所帮助 ;)