Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
315 views
in Technique[技术] by (71.8m points)

javascript - Set minimum delay on bluebird.js promise resolution

I want to guarantee a minimum delay on the resolution of a bluebird.js promise.

As an example, let's say I'm making a request wrapped in a promise. The behaviour I want is that if the request takes less than 5 seconds, I want to artificially increase the delay of the promise resolution to 5 seconds. If the request were to take more than 5 seconds, I'd want no artificial delay added - so it's a little more complicated than just adding a static delay to every request. All of this should be completely hidden from the consumer of the promise - they should just see the promise being resolved in 5 seconds, or more.

To demonstrate, I've got a simple mock implementation example which hardcodes the mocked request latency at 3 seconds.

My first try went something like this - using a setTimeout to ensure the resolve callback isn't called before 5 seconds have passed.

fiddle here

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);

A lot of boilerplate.

I then discovered through this answer that I could use the Promise.join function to join the promise wrapping the request with a Promise.delay of the minimum 5 second delay to achieve the same thing:

fiddle here

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);

This is cleaner, but still a little more boilerplate than I'd like - I've dug around the bluebird api reference and can't find a function which does this directly.

My question is simple - can anybody suggest a cleaner, more declarative way of achieving this behaviour with bluebird than the second example?

Any suggestions of other promise libraries where the api does offer this would also be appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I believe that all you need to do is Promise.delay(value).return(promise):

You can wrap it in a utility function:

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/

Note that one thing about this is that if the promise rejects, the delayed promise will not reject until the five seconds have elapsed. This may be the desired behavior (as @Benjamin Gruenbaum points out in the comments), but if you would prefer for it to reject immediately, two other options are:

With 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);
}

Or @Benjamin Gruenbaum's approach with Promise.all:

function minDelay(promise, delay) {
    Promise.all([promise, Promise.delay(delay)]).get(0);
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...