Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What about a throttle object? #943

Closed
ghost opened this issue Oct 26, 2015 · 6 comments
Closed

What about a throttle object? #943

ghost opened this issue Oct 26, 2015 · 6 comments
Labels

Comments

@ghost
Copy link

ghost commented Oct 26, 2015

Hi,

one thing that would be useful (to me at any rate) is an object similar to a queue but that rejects attempts to add tasks unless there are no tasks executing (asynchronously). Put another way, if you add a task, attempts to add further tasks will fail until the existing and only task has finished or an error occurs.

I can give a case study. Suppose you want to write a collaborative editor for the browser. You want to execute an update task whenever a key is pressed, say, or periodically in the background say every second. So if an update task is executing, others can be safely rejected.

In essence a throttle (as I call it) is a bit like queue of length at most one. In this case study it effectively throttles network activity, hence the name, and in fact also assures that that activity happens sequentially, which makes life easier, too, as you know.

Kind regards,

James

@ghost ghost changed the title What about a throttle method? What about a throttle object? Oct 26, 2015
@megawac
Copy link
Collaborator

megawac commented Oct 26, 2015

I think I would be more comfortable delegating this to a third party library (they are extremely well ironed out in lodash/underscore). Throttle is a tricky method as you have to consider timezone constraints, timeouts, various options, etc.

Unless you can think of a case that would be cleaner having this functionality in async itself I think its better left to existing implementations. That said, it could be a good idea to add a related topic to the readme

https://lodash.com/docs#throttle
http://underscorejs.org/#throttle

@ghost
Copy link
Author

ghost commented Feb 3, 2016

Hi,

thanks for getting back to me, firstly, and sorry it's taken me so long to reply.

In the end I just implemented a simple queue myself (queue.js) and added what I called a bottle neck on top of that (bottleNeck.js).

And you're right, stuff like this is too specific to find it's way into async.

async.zip

I've now rolled my own modules to deal with this stuff although I keep async for the unit tests. Working with async has helped my understanding of JavaScript, which is always good. I'm grateful!

Kind regards,

James

@aearly
Copy link
Collaborator

aearly commented Mar 6, 2016

There is a bit of demand for something like this. I think the problem in #1020 could have been solved by some sort of throttled queue.

@megawac
Copy link
Collaborator

megawac commented Mar 6, 2016

@aearly as I described in that thread he wasn't looking for a throttle

@aearly
Copy link
Collaborator

aearly commented Mar 6, 2016

Ah, I might be confused. He wanted staggering of execution, which seemed like it could be accomplished with queued task functions in a throttled queue.

@ghost
Copy link
Author

ghost commented Mar 7, 2016

Hi,

yeah, I think we had this covered but thanks for the feedback.

I have a queue:

var Queue = function() {
  this.tasks = [];
};

Queue.prototype = {
  isEmpty: function() {
    var tasksLength = this.tasks.length,
        empty = tasksLength === 0;  ///

    return empty;
  },

  push: function(method, cb) {
    var task = {
      method: method,
      cb: cb
    };

    var empty = this.isEmpty();

    this.tasks.push(task);

    if (empty) {
      method(this.cb.bind(this));
    }
  },

  cb: function() {
    var task = this.tasks.shift(),
        cb = task.cb;

    if (cb) {
      cb.apply(null, arguments);
    }

    this.next();
  },

  next: function() {
    var empty = this.isEmpty();

    if (!empty) {
      var task = first(this.tasks),
          method = task.method;

      method(this.cb.bind(this));
    }
  }
}

function first(array) { return array[0]; }

And on top of that I have what I call a bottleneck, which I thought was a better name for what I wanted:

var BottleNeck = function() {
  this.queue = new Queue();
};

BottleNeck.prototype = {
  push: function(method, cb) {
    this.queue.push(method, cb);
  },

  force: function(method, cb) {
    var forced,
        queueIsEmpty = this.queue.isEmpty();

    if (queueIsEmpty) {
      this.queue.push(method, cb);

      forced = true;
    } else {
      forced = false;
    }

    return forced;
  }
};

The queue allows me to enforce sequentiality on the client, the bottleneck on top allows me to 'force' tasks. Forced tasks are only executed, and executed immediately, if the underlying queue is empty. Otherwise they are discarded. The forced() method will tell you whether or not it was successful in forcing the task, so to speak. On the other hand pushed tasks are just queued.

You would want tasks to add and remove files from a session to be pushed, for example, whereas periodic session update tasks you would want to be forced.

async could have given me the queue but in the end I rolled my own, you know how it is.

Regards,

James

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants