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 at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15goog.provide('goog.async.run');
16
17goog.require('goog.async.WorkQueue');
18goog.require('goog.async.nextTick');
19goog.require('goog.async.throwException');
20
21
22/**
23 * Fires the provided callback just before the current callstack unwinds, or as
24 * soon as possible after the current JS execution context.
25 * @param {function(this:THIS)} callback
26 * @param {THIS=} opt_context Object to use as the "this value" when calling
27 * the provided function.
28 * @template THIS
29 */
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 }
39
40 goog.async.run.workQueue_.add(callback, opt_context);
41};
42
43
44/**
45 * Initializes the function to use to process the work queue.
46 * @private
47 */
48goog.async.run.initializeRunner_ = function() {
49 // If native Promises are available in the browser, just schedule the callback
50 // on a fulfilled promise, which is specified to be async, but as fast as
51 // possible.
52 if (goog.global.Promise && goog.global.Promise.resolve) {
53 var promise = goog.global.Promise.resolve(undefined);
54 goog.async.run.schedule_ = function() {
55 promise.then(goog.async.run.processWorkQueue);
56 };
57 } else {
58 goog.async.run.schedule_ = function() {
59 goog.async.nextTick(goog.async.run.processWorkQueue);
60 };
61 }
62};
63
64
65/**
66 * Forces goog.async.run to use nextTick instead of Promise.
67 *
68 * This should only be done in unit tests. It's useful because MockClock
69 * replaces nextTick, but not the browser Promise implementation, so it allows
70 * Promise-based code to be tested with MockClock.
71 *
72 * However, we also want to run promises if the MockClock is no longer in
73 * control so we schedule a backup "setTimeout" to the unmocked timeout if
74 * provided.
75 *
76 * @param {function(function())=} opt_realSetTimeout
77 */
78goog.async.run.forceNextTick = function(opt_realSetTimeout) {
79 goog.async.run.schedule_ = function() {
80 goog.async.nextTick(goog.async.run.processWorkQueue);
81 if (opt_realSetTimeout) {
82 opt_realSetTimeout(goog.async.run.processWorkQueue);
83 }
84 };
85};
86
87
88/**
89 * The function used to schedule work asynchronousely.
90 * @private {function()}
91 */
92goog.async.run.schedule_;
93
94
95/** @private {boolean} */
96goog.async.run.workQueueScheduled_ = false;
97
98
99/** @private {!goog.async.WorkQueue} */
100goog.async.run.workQueue_ = new goog.async.WorkQueue();
101
102
103if (goog.DEBUG) {
104 /**
105 * Reset the work queue. Only available for tests in debug mode.
106 */
107 goog.async.run.resetQueue = function() {
108 goog.async.run.workQueueScheduled_ = false;
109 goog.async.run.workQueue_ = new goog.async.WorkQueue();
110 };
111}
112
113
114/**
115 * Run any pending goog.async.run work items. This function is not intended
116 * for general use, but for use by entry point handlers to run items ahead of
117 * goog.async.nextTick.
118 */
119goog.async.run.processWorkQueue = function() {
120 // NOTE: additional work queue items may be added while processing.
121 var item = null;
122 while (item = goog.async.run.workQueue_.remove()) {
123 try {
124 item.fn.call(item.scope);
125 } catch (e) {
126 goog.async.throwException(e);
127 }
128 goog.async.run.workQueue_.returnUnused(item);
129 }
130
131 // There are no more work items, allow processing to be scheduled again.
132 goog.async.run.workQueueScheduled_ = false;
133};