Path: blob/trunk/third_party/closure/goog/async/run.js
4096 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/5goog.module('goog.async.run');6goog.module.declareLegacyNamespace();78const WorkQueue = goog.require('goog.async.WorkQueue');9const asyncStackTag = goog.require('goog.debug.asyncStackTag');10const nextTick = goog.require('goog.async.nextTick');11const throwException = goog.require('goog.async.throwException');1213/**14* @define {boolean} If true, use the global Promise to implement run15* assuming either the native, or polyfill version will be used. Does still16* permit tests to use forceNextTick.17*/18goog.ASSUME_NATIVE_PROMISE = goog.define('goog.ASSUME_NATIVE_PROMISE', false);1920/**21* The function used to schedule work asynchronousely.22* @private {function()}23*/24let schedule;2526/** @private {boolean} */27let workQueueScheduled = false;2829/** @type {!WorkQueue} */30let workQueue = new WorkQueue();3132/**33* Fires the provided callback just before the current callstack unwinds, or as34* soon as possible after the current JS execution context.35* @param {function(this:THIS)} callback36* @param {THIS=} context Object to use as the "this value" when calling the37* provided function.38* @template THIS39*/40let run = (callback, context = undefined) => {41if (!schedule) {42initializeRunner();43}44if (!workQueueScheduled) {45// Nothing is currently scheduled, schedule it now.46schedule();47workQueueScheduled = true;48}49callback = asyncStackTag.wrap(callback, 'goog.async.run');5051workQueue.add(callback, context);52};5354/** Initializes the function to use to process the work queue. */55let initializeRunner = () => {56if (goog.ASSUME_NATIVE_PROMISE ||57(goog.global.Promise && goog.global.Promise.resolve)) {58// Use goog.global.Promise instead of just Promise because the relevant59// externs may be missing, and don't alias it because this could confuse the60// compiler into thinking the polyfill is required when it should be treated61// as optional.62const promise = goog.global.Promise.resolve(undefined);63schedule = () => {64promise.then(run.processWorkQueue);65};66} else {67schedule = () => {68nextTick(run.processWorkQueue);69};70}71};7273/**74* Forces run to use nextTick instead of Promise.75* This should only be done in unit tests. It's useful because MockClock76* replaces nextTick, but not the browser Promise implementation, so it allows77* Promise-based code to be tested with MockClock.78* However, we also want to run promises if the MockClock is no longer in79* control so we schedule a backup "setTimeout" to the unmocked timeout if80* provided.81* @param {function(function())=} realSetTimeout82*/83run.forceNextTick = (realSetTimeout = undefined) => {84schedule = () => {85nextTick(run.processWorkQueue);86if (realSetTimeout) {87realSetTimeout(run.processWorkQueue);88}89};90};9192if (goog.DEBUG) {93/** Reset the work queue. Only available for tests in debug mode. */94run.resetQueue = () => {95workQueueScheduled = false;96workQueue = new WorkQueue();97};9899/** Resets the scheduler. Only available for tests in debug mode. */100run.resetSchedulerForTest = () => {101initializeRunner();102};103}104105/**106* Run any pending run work items. This function is not intended107* for general use, but for use by entry point handlers to run items ahead of108* nextTick.109*/110run.processWorkQueue = () => {111// NOTE: additional work queue items may be added while processing.112let item = null;113while (item = workQueue.remove()) {114try {115item.fn.call(item.scope);116} catch (e) {117throwException(e);118}119workQueue.returnUnused(item);120}121122// There are no more work items, allow processing to be scheduled again.123workQueueScheduled = false;124};125126exports = run;127128129