设置蓝鸟承诺分辨率.js最小延迟

Set minimum delay on bluebird.js promise resolution

本文关键字:延迟 js 分辨率 蓝鸟 承诺 设置      更新时间:2023-09-26

我想保证蓝鸟.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);

很多样板。

然后我通过这个答案发现,我可以使用 Promise.join 函数来加入 promise 包装请求,并用至少 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);

这更干净,但仍然比我想要的更样板 - 我已经挖掘了蓝鸟 api 参考,找不到直接执行此操作的函数。

我的问题很简单 - 有人能提出一种比第二个例子更干净、更声明性的方式来实现蓝鸟的这种行为吗?

对于

API 确实提供此功能的其他承诺库的任何建议也将不胜感激。

我相信

您需要做的就是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/

请注意,关于这一点的一件事是,如果承诺拒绝,延迟的承诺在五秒钟过去之前不会拒绝。这可能是期望的行为(正如 Gruenbaum 在评论中指出@Benjamin),但如果您希望它立即拒绝,另外两个选项是:

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 秒调用的承诺在这里无关紧要,它不应该是承诺的一部分。虽然我很受宠若惊,但你喜欢我的回答.join但它也不是我在这里实际使用的工具。

首先,API 调用只是一个任意的承诺返回函数。

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

下面是一个最小延迟承诺的代码示例:

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;

pMinDelay函数导入您想要的位置,延迟承诺。

我希望这对;)有所帮助

库 spex 是专门为处理使用 promise 时的数据限制和负载平衡等问题而编写的。

在您的情况下,我们可以使用以下示例:

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,因此可以在需要时双向处理负载平衡。

此外,您可以使用具有相同负载平衡策略的方法页,但在页中处理请求。