# lib/goog/async/run.js

    1// Copyright 2013 The Closure Library Authors. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS-IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415goog.provide('goog.async.run');1617goog.require('goog.async.nextTick');18goog.require('goog.async.throwException');19goog.require('goog.testing.watchers');202122/**23 * Fires the provided callback just before the current callstack unwinds, or as24 * soon as possible after the current JS execution context.25 * @param {function(this:THIS)} callback26 * @param {THIS=} opt_context Object to use as the "this value" when calling27 *     the provided function.28 * @template THIS29 */30goog.async.run = function(callback, opt_context) {31  if (!goog.async.run.schedule_) {32    goog.async.run.initializeRunner_();33  }34  if (!goog.async.run.workQueueScheduled_) {35    // Nothing is currently scheduled, schedule it now.36    goog.async.run.schedule_();37    goog.async.run.workQueueScheduled_ = true;38  }3940  goog.async.run.workQueue_.push(41      new goog.async.run.WorkItem_(callback, opt_context));42};434445/**46 * Initializes the function to use to process the work queue.47 * @private48 */49goog.async.run.initializeRunner_ = function() {50  // If native Promises are available in the browser, just schedule the callback51  // on a fulfilled promise, which is specified to be async, but as fast as52  // possible.53  if (goog.global.Promise && goog.global.Promise.resolve) {54    var promise = goog.global.Promise.resolve();55    goog.async.run.schedule_ = function() {56      promise.then(goog.async.run.processWorkQueue);57    };58  } else {59    goog.async.run.schedule_ = function() {60      goog.async.nextTick(goog.async.run.processWorkQueue);61    };62  }63};646566/**67 * Forces goog.async.run to use nextTick instead of Promise.68 *69 * This should only be done in unit tests. It's useful because MockClock70 * replaces nextTick, but not the browser Promise implementation, so it allows71 * Promise-based code to be tested with MockClock.72 */73goog.async.run.forceNextTick = function() {74  goog.async.run.schedule_ = function() {75    goog.async.nextTick(goog.async.run.processWorkQueue);76  };77};787980/**81 * The function used to schedule work asynchronousely.82 * @private {function()}83 */84goog.async.run.schedule_;858687/** @private {boolean} */88goog.async.run.workQueueScheduled_ = false;899091/** @private {!Array.<!goog.async.run.WorkItem_>} */92goog.async.run.workQueue_ = [];939495if (goog.DEBUG) {96  /**97   * Reset the event queue.98   * @private99   */100  goog.async.run.resetQueue_ = function() {101    goog.async.run.workQueueScheduled_ = false;102    goog.async.run.workQueue_ = [];103  };104105  // If there is a clock implemenation in use for testing106  // and it is reset, reset the queue.107  goog.testing.watchers.watchClockReset(goog.async.run.resetQueue_);108}109110111/**112 * Run any pending goog.async.run work items. This function is not intended113 * for general use, but for use by entry point handlers to run items ahead of114 * goog.async.nextTick.115 */116goog.async.run.processWorkQueue = function() {117  // NOTE: additional work queue items may be pushed while processing.118  while (goog.async.run.workQueue_.length) {119    // Don't let the work queue grow indefinitely.120    var workItems = goog.async.run.workQueue_;121    goog.async.run.workQueue_ = [];122    for (var i = 0; i < workItems.length; i++) {123      var workItem = workItems[i];124      try {125        workItem.fn.call(workItem.scope);126      } catch (e) {127        goog.async.throwException(e);128      }129    }130  }131132  // There are no more work items, reset the work queue.133  goog.async.run.workQueueScheduled_ = false;134};135136137138/**139 * @constructor140 * @final141 * @struct142 * @private143 *144 * @param {function()} fn145 * @param {Object|null|undefined} scope146 */147goog.async.run.WorkItem_ = function(fn, scope) {148  /** @const */ this.fn = fn;149  /** @const */ this.scope = scope;150};