Search code examples
javascriptpromisebluebird

Creating throttling function with Bluebird promises


I am attempting to created a throttling function. I have looked at a few SO posts and copied some code but I am unable to get it to delay.

Basically I have a number of methods in a class that need to call the Amazon API. They all make use of a common function - doCall which I implemented as follows:

Amazon.prototype.doCall = function(func) {
  var self = this;

  var queue = P.resolve();

  function throttle(fn) {
    var res = queue.then(function() { // wait for queue
      return fn(); // call the function
    });

    queue = P.delay(61000).return(queue); // make the queue wait for 61 seconds
    return res; // return the result
  }


  // Create instance of MWS client
  if (!this.client) {
    this.client = new mws.Client(key, secret, merchant, {});
  }

  var call = function() {

    // The library uses a weird signature so I am wrapping it thus
    return new P(function(resolve, reject) {

      // The original MWS library call
      self.client.invoke(func, function(r, e) {
        // ... stuff
        resolve(r);
      });
    });

  };
  return throttle(call);
};

Basically I fetch order lists and orders and need to delay each call 60+ seconds. Right now it all happens without a delay whatsoever. Suggestions?

I am basically using it like this (contrived but should give the idea)

self.doCall(ListOrders).then(function(res) { 
  // parse results
  self.doCall(ListMoreOrdersByPage).then(function(res) {
     // Now I might go through each and fetch details
     var ids = [...] // Parse result for ids
     return P.map(ids, function(id) {
       return doCall(GetOrderById);
     });  
     ....

Solution

  • Your problem is that

    Amazon.prototype.doCall = function(func) {
        var queue = P.resolve();
        …
    

    means that you are re-creating a new queue on every call of that method. Not really helpful. Instead you probably want one queue per Amazon instead, so put the initialisation in the constructor.

    I've also simplified your code a bit:

    function Amazon(…) {
      …
      this.queue = P.resolve();
    }
    
    Amazon.prototype.doCall = function(func) {
      if (!this.client) {
        // Create instance of MWS client
        this.client = new mws.Client(key, secret, merchant, {});
      }
    
      var self = this;
      var res = this.queue.then(function() {
        // The library uses a weird signature so I am wrapping it thus
        return new P(function(resolve, reject) {
          self.client.invoke(func, function(r, e) {
            // ... stuff
            resolve(r);
          });
        });
      });
    
      this.queue = this.queue.delay(610000); // make the queue wait for 61s
      // if you want to make it wait *between* calls, use
      // this.queue = res.catch(function(){}).delay(610000);
      return res;
    };