3
\$\begingroup\$

Based on the answer to my previous question on Stack Overflow, I put together the following Queue class. I realize there are already libraries out there to do this. However, I wanted to actually understand the concept and implementing it yourself really drives the knowledge home (it also provides much more fun and a greater sense of accomplishment!).

But I DO want to make sure I've done this in the best way. Please see if it has any obvious issues or lacks any necessary features. A few possible improvements:

  1. Allow the list of functions passed to the constructor to be separate arguemnts, rather than an array of objects, by converting the arguments object into an array.

    One potential problem with this is if, in answer to #6 below, you guys feel the constructor should accept a context to be applied as this when calling the queued functions. To my mind, it seems more intuitive to have var Queue = function( arrayOfFuncs, context ) { than this:

    var Queue = function() { var list = Array.prototype.slice.call( arguments ), context = this, self = this; if (typeof list[0] !== 'function') { context = list.shift(); // Context would have to be the first argument passed in } // ... }; 
  2. Similarly deal with returnValue as an array, so that a variable number of arguments could be passed to the callback.

  3. Adding onComplete or onError handlers, allowing easier access to the final return value.
  4. Add more helper functions like 'delay'. (For instance, a 'timer' method which handles an array, sending the items in the array to a specified processing function in batches. I.e. a fancy way to perform common setInterval patterns.)
  5. Perhaps methods to add or delete items from the queue?
  6. What should be passed as this to each function in the queue? Right now I am passing the queue itself, to allow the functions access to this.paused if they want to pause an interval (i.e. keep returning from that interval until no longer paused)

What else should a good queue have?

var Queue = function(queue) { var list = queue, self = this; // Use scope to allow Queue.next() to reference 'this' this.paused = false; // Ensures Queue.start() doesn't skip a currently running interval when // resuming from pause this.running = false; // Allows clearing of timeout / interval this.current = null; this.returnValue; this.next = function(returnValue) { self.running = false; if (list.length === 0) { self.returnValue = returnValue; return; } // Grab next function in queue and add returnvalue to args if needed var next = list.shift(); if (returnValue) next.args.push( returnValue ); if (self.paused) { // Stick the function back at beginning of queue to await restart list.unshift( next ); return; } else { self.running = true; // Call next function, applying the Queue as 'this' self.current = next.fn.apply(self, next.args); } }; for (var i = 0; i < list.length; i++) { // Ensure there is an args array, then add Queue.next() as callback if (!list[i].args) list[i].args = []; list[i].args.push( this.next ); // Allows easier method of adding delays and pauses to queue if (typeof list[i].fn === 'string') { list[i].fn = this[ list[i].fn ]; } } }; Queue.prototype = { delay : function(time, callback, returnValue) { // Allows leaving out the args array and using default 1000ms // Remaps arguments if it has been left off if (typeof callback !== 'function') { returnValue = callback; callback = time; time = 1000; } return setTimeout( function() { callback( returnValue ); }, +time ); }, pause : function(callback, returnValue) { this.paused = true; // Allows Queue.pause() to be called either in the queue or while the // queue is running, in which case no callback need be specified if (callback) { callback( returnValue ); } }, advance : function() { // Executes queue items one at a time // Clear via both types, to ensure it's actually cleared clearInterval( this.current ); clearTimeout( this.current ); this.paused = false; this.next(); this.paused = true; }, start : function() { this.paused = false; // As stated earlier, a currently running interval will be completed // rather than skipping to the next queued function if (!this.running) this.next(); } }; function $(id) {return document.getElementById(id);} // Helper function type(from, to, callback, returnValue) { // Example async function // Shows that the return value from the first call of this function // carries through; notice 'returnValue' comes after 'callback' console.log(returnValue); var chars = from.value.split(''), self = this, interval = setInterval( function() { // If the following line is commented, it allows us to use Queue.advance() // If uncommented, we can use Queue.pause() and .start() // to pause and resume interval /* if (self.paused) return; */ to.value += chars.shift(); if (chars.length === 0) { clearInterval(interval); // Return a value by passing as param to callback callback(to.value); } }, 35); // Return an ID to allow Queue.advance() to clear the interval or timeout return interval; } window.testQueue = new Queue([ { fn: type, args: [$('textarea1'), $('textarea2')] }, { fn: 'delay', args: [3000] }, // Try with or without the args array { fn: 'pause' }, { fn: type, args: [$('textarea3'), $('textarea4')] } ]); // Start it, or use Queue.advance() from the console //testQueue.start(); // When Queue.pause() is reached in the queue, manually call Queue.start() 
\$\endgroup\$
0

    1 Answer 1

    2
    \$\begingroup\$

    From a once over,

    • I would check for if (self.paused) { at the very beginning, otherwise you might keep pushing returnValue into the args
    • I would not assign an anonymous function herre: this.next = function(returnValue) { I would go for this.next = function next(returnValue) { this will make life easier when looking at stacktraces
    • It does not make sense to maintain both self.running and self.paused since self.running will always be !self.paused
    • I would write if (!list[i].args) list[i].args = []; as
      list[i].args = list[i].args || []
    • JsHint has nothing to report

    On the whole though, I am not sure I would use this library. I think I would go hunt for an existing queue library.

    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.