190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// found in the LICENSE file. 490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)'use strict'; 690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Namespace for async utility functions. 990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var AsyncUtil = {}; 1190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * Asynchronous version of Array.forEach. 143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * This executes a provided function callback once per array element, then 153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * run completionCallback to notify the completion. 163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * The callback can be an asynchronous function, but the execution is 173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * sequentially done. 183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * 193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {Array.<T>} array The array to be iterated. 203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {function(function(), T, number, Array.<T>} callback The iteration 213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * callback. The first argument is a callback to notify the completion of 223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * the iteration. 233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {function()} completionCallback Called when all iterations are 243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * completed. 253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {Object=} opt_thisObject Bound to callback if given. 263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @template T 273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) */ 283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)AsyncUtil.forEach = function( 293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) array, callback, completionCallback, opt_thisObject) { 303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) if (opt_thisObject) 313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) callback = callback.bind(opt_thisObject); 323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) var queue = new AsyncUtil.Queue(); 343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) for (var i = 0; i < array.length; i++) { 353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) queue.run(function(element, index, iterationCompletionCallback) { 363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) callback(iterationCompletionCallback, element, index, array); 373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) }.bind(null, array[i], i)); 383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) } 393551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) queue.run(function(iterationCompletionCallback) { 403551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) completionCallback(); // Don't pass iteration completion callback. 413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) }); 423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}; 433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)/** 4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Creates a class for executing several asynchronous closures in a fifo queue. 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Added tasks will be started in order they were added. Tasks are run 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * concurrently. At most, |limit| jobs will be run at the same time. 4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {number} limit The number of jobs to run at the same time. 5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @constructor 5190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue = function(limit) { 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) console.assert(limit > 0, '|limit| must be larger than 0'); 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.limit_ = limit; 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.addedTasks_ = []; 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTasks_ = []; 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.isCancelled_ = false; 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) Object.seal(this); 6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 6390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @return {boolean} True when a task is running, otherwise false. 654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */ 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.isRunning = function() { 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.pendingTasks_.length !== 0; 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {number} Number of waiting tasks. 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.getWaitingTasksCount = function() { 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.addedTasks_.length; 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} Number of running tasks. 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.getRunningTasksCount = function() { 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.pendingTasks_.length; 824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}; 834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/** 8590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Enqueues a closure to be executed. 86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {function(function())} closure Closure with a completion 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * callback to be executed. 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.run = function(closure) { 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.isCancelled_) { 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) console.error('Queue is calcelled. Cannot add a new task.'); 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.addedTasks_.push(closure); 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.continue_(); 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Cancels the queue. It removes all the not-run (yet) tasks. Note that this 101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * does NOT stop tasks currently running. 10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.cancel = function() { 104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.isCancelled_ = true; 105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.addedTasks_ = []; 10690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 10790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 10890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @return {boolean} True when the queue have been requested to cancel or is 110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * already cancelled. Otherwise false. 111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.isCancelled = function() { 113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return this.isCancelled_; 114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Runs the next tasks if available. 11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @private 11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.continue_ = function() { 121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.addedTasks_.length === 0) 122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) return; 123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) console.assert( 125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTasks_.length <= this.limit_, 126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 'Too many jobs are running (' + this.pendingTasks_.length + ')'); 127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (this.pendingTasks_.length >= this.limit_) 12990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return; 13090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 13190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Run the next closure. 132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var closure = this.addedTasks_.shift(); 133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTasks_.push(closure); 134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) closure(this.onTaskFinished_.bind(this, closure)); 135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.continue_(); 13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 13890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 13990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Called when a task is finished. Removes the tasks from pending task list. 141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @param {function()} closure Finished task, which has been bound in 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * |continue_|. 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @private 1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */ 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.ConcurrentQueue.prototype.onTaskFinished_ = function(closure) { 146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) var index = this.pendingTasks_.indexOf(closure); 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) console.assert(index >= 0, 'Invalid task is finished'); 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.pendingTasks_.splice(index, 1); 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) this.continue_(); 1514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}; 1524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/** 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Creates a class for executing several asynchronous closures in a fifo queue. 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Added tasks will be executed sequentially in order they were added. 156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @constructor 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * @extends {AsyncUtil.ConcurrentQueue} 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) */ 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)AsyncUtil.Queue = function() { 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) AsyncUtil.ConcurrentQueue.call(this, 1); 162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAsyncUtil.Queue.prototype = { 1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci __proto__: AsyncUtil.ConcurrentQueue.prototype 1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/** 16990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Creates a class for executing several asynchronous closures in a group in 17090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * a dependency order. 17190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * 17290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @constructor 17390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 17490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AsyncUtil.Group = function() { 17590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.addedTasks_ = {}; 17690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.pendingTasks_ = {}; 17790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.finishedTasks_ = {}; 17890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.completionCallbacks_ = []; 17990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 18090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 18190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 18290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Enqueues a closure to be executed after dependencies are completed. 18390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * 18490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {function(function())} closure Closure with a completion callback to 18590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * be executed. 18690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {Array.<string>=} opt_dependencies Array of dependencies. If no 18790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * dependencies, then the the closure will be executed immediately. 18890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {string=} opt_name Task identifier. Specify to use in dependencies. 18990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 19090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AsyncUtil.Group.prototype.add = function(closure, opt_dependencies, opt_name) { 19190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var length = Object.keys(this.addedTasks_).length; 19290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var name = opt_name || ('(unnamed#' + (length + 1) + ')'); 19390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 19490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var task = { 19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) closure: closure, 19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dependencies: opt_dependencies || [], 19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) name: name 19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) }; 19990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 20090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.addedTasks_[name] = task; 20190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.pendingTasks_[name] = task; 20290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 20390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 20490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 20590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Runs the enqueued closured in order of dependencies. 20690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * 20790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {function()=} opt_onCompletion Completion callback. 20890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 20990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AsyncUtil.Group.prototype.run = function(opt_onCompletion) { 21090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (opt_onCompletion) 21190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.completionCallbacks_.push(opt_onCompletion); 21290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.continue_(); 21390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 21490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 21590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 21690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Runs enqueued pending tasks whose dependencies are completed. 21790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @private 21890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 21990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AsyncUtil.Group.prototype.continue_ = function() { 22090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // If all of the added tasks have finished, then call completion callbacks. 22190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (Object.keys(this.addedTasks_).length == 22290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) Object.keys(this.finishedTasks_).length) { 22390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (var index = 0; index < this.completionCallbacks_.length; index++) { 22490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var callback = this.completionCallbacks_[index]; 22590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) callback(); 22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 22790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.completionCallbacks_ = []; 22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) return; 22990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 23090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 23190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (var name in this.pendingTasks_) { 23290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var task = this.pendingTasks_[name]; 23390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var dependencyMissing = false; 23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) for (var index = 0; index < task.dependencies.length; index++) { 23590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) var dependency = task.dependencies[index]; 23690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Check if the dependency has finished. 23790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!this.finishedTasks_[dependency]) 23890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) dependencyMissing = true; 23990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 24090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // All dependences finished, therefore start the task. 24190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (!dependencyMissing) { 24290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) delete this.pendingTasks_[task.name]; 24390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) task.closure(this.finish_.bind(this, task)); 24490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 24590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) } 24690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 24790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 24890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/** 24990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Finishes the passed task and continues executing enqueued closures. 25090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * 25190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @param {Object} task Task object. 25290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * @private 25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */ 25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AsyncUtil.Group.prototype.finish_ = function(task) { 25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.finishedTasks_[task.name] = task; 25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this.continue_(); 25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 258868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 259868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)/** 2601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Aggregates consecutive calls and executes the closure only once instead of 2611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * several times. The first call is always called immediately, and the next 2621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * consecutive ones are aggregated and the closure is called only once once 2631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * |delay| amount of time passes after the last call to run(). 2641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * 2651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function()} closure Closure to be aggregated. 2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number=} opt_delay Minimum aggregation time in milliseconds. Default 2671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * is 50 milliseconds. 2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @constructor 2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 2701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAsyncUtil.Aggregator = function(closure, opt_delay) { 2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** 2721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @type {number} 2731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private 2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 2751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.delay_ = opt_delay || 50; 2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** 2781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @type {function()} 2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private 2801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.closure_ = closure; 2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** 2841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @type {number?} 2851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private 2861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 2871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.scheduledRunsTimer_ = null; 2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** 2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @type {number} 2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private 2921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.lastRunTime_ = 0; 2941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 2951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Runs a closure. Skips consecutive calls. The first call is called 2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * immediately. 2991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 3001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAsyncUtil.Aggregator.prototype.run = function() { 3011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // If recently called, then schedule the consecutive call with a delay. 3021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (Date.now() - this.lastRunTime_ < this.delay_) { 3031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.cancelScheduledRuns_(); 3041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.scheduledRunsTimer_ = setTimeout(this.runImmediately_.bind(this), 3051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.delay_ + 1); 3061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.lastRunTime_ = Date.now(); 3071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return; 3081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 3091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Otherwise, run immediately. 3111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.runImmediately_(); 3121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 3131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 3151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Calls the schedule immediately and cancels any scheduled calls. 3161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private 3171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 3181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAsyncUtil.Aggregator.prototype.runImmediately_ = function() { 3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.cancelScheduledRuns_(); 3201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.closure_(); 3211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.lastRunTime_ = Date.now(); 3221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 3231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 3251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Cancels all scheduled runs (if any). 3261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @private 3271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci */ 3281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciAsyncUtil.Aggregator.prototype.cancelScheduledRuns_ = function() { 3291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (this.scheduledRunsTimer_) { 3301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci clearTimeout(this.scheduledRunsTimer_); 3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.scheduledRunsTimer_ = null; 3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } 3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}; 3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 3351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/** 336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Samples calls so that they are not called too frequently. 337f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * The first call is always called immediately, and the following calls may 338f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * be skipped or delayed to keep each interval no less than |minInterval_|. 339868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * 340f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {function()} closure Closure to be called. 341f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @param {number=} opt_minInterval Minimum interval between each call in 342f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * milliseconds. Default is 200 milliseconds. 343868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @constructor 344868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 345f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)AsyncUtil.RateLimiter = function(closure, opt_minInterval) { 346868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) /** 347f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @type {function()} 348868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @private 349868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 350f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.closure_ = closure; 351868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 352868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) /** 353f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @type {number} 354868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @private 355868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 356f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.minInterval_ = opt_minInterval || 200; 357868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 358868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) /** 359f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * @type {number} 360868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @private 361868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 362f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.scheduledRunsTimer_ = 0; 363868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 364868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) /** 365f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * This variable remembers the last time the closure is called. 366868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @type {number} 367868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @private 368868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 369868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) this.lastRunTime_ = 0; 370f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) Object.seal(this); 372868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 373868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 374868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)/** 375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Requests to run the closure. 376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Skips or delays calls so that the intervals between calls are no less than 377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * |minInteval_| milliseconds. 378868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)AsyncUtil.RateLimiter.prototype.run = function() { 380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) var now = Date.now(); 381f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // If |minInterval| has not passed since the closure is run, skips or delays 382f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // this run. 383f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (now - this.lastRunTime_ < this.minInterval_) { 384f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // Delays this run only when there is no scheduled run. 385f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // Otherwise, simply skip this run. 386f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (!this.scheduledRunsTimer_) { 387f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.scheduledRunsTimer_ = setTimeout( 388f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.runImmediately.bind(this), 389f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.lastRunTime_ + this.minInterval_ - now); 390f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 391868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return; 392868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 393868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 394f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // Otherwise, run immediately 395f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.runImmediately(); 396868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 397868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 398868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)/** 399f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * Calls the scheduled run immediately and cancels any scheduled calls. 400868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 401f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)AsyncUtil.RateLimiter.prototype.runImmediately = function() { 402868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) this.cancelScheduledRuns_(); 403868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) this.closure_(); 404868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) this.lastRunTime_ = Date.now(); 405868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 406868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 407868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)/** 408868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * Cancels all scheduled runs (if any). 409868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * @private 410868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) */ 411f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)AsyncUtil.RateLimiter.prototype.cancelScheduledRuns_ = function() { 412868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (this.scheduledRunsTimer_) { 413868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) clearTimeout(this.scheduledRunsTimer_); 414f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) this.scheduledRunsTimer_ = 0; 415868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 416868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}; 417