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');
20goog.require('goog.testing.watchers');
21
22
23/**
24 * Fires the provided callback just before the current callstack unwinds, or as
25 * soon as possible after the current JS execution context.
26 * @param {function(this:THIS)} callback
27 * @param {THIS=} opt_context Object to use as the "this value" when calling
28 * the provided function.
29 * @template THIS
30 */
31goog.async.run = function(callback, opt_context) {
32 if (!goog.async.run.schedule_) {
33 goog.async.run.initializeRunner_();
34 }
35 if (!goog.async.run.workQueueScheduled_) {
36 // Nothing is currently scheduled, schedule it now.
37 goog.async.run.schedule_();
38 goog.async.run.workQueueScheduled_ = true;
39 }
40
41 goog.async.run.workQueue_.add(callback, opt_context);
42};
43
44
45/**
46 * Initializes the function to use to process the work queue.
47 * @private
48 */
49goog.async.run.initializeRunner_ = function() {
50 // If native Promises are available in the browser, just schedule the callback
51 // on a fulfilled promise, which is specified to be async, but as fast as
52 // 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};
64
65
66/**
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 MockClock
70 * replaces nextTick, but not the browser Promise implementation, so it allows
71 * Promise-based code to be tested with MockClock.
72 *
73 * However, we also want to run promises if the MockClock is no longer in
74 * control so we schedule a backup "setTimeout" to the unmocked timeout if
75 * provided.
76 *
77 * @param {function(function())=} opt_realSetTimeout
78 */
79goog.async.run.forceNextTick = function(opt_realSetTimeout) {
80 goog.async.run.schedule_ = function() {
81 goog.async.nextTick(goog.async.run.processWorkQueue);
82 if (opt_realSetTimeout) {
83 opt_realSetTimeout(goog.async.run.processWorkQueue);
84 }
85 };
86};
87
88
89/**
90 * The function used to schedule work asynchronousely.
91 * @private {function()}
92 */
93goog.async.run.schedule_;
94
95
96/** @private {boolean} */
97goog.async.run.workQueueScheduled_ = false;
98
99
100/** @private {!goog.async.WorkQueue} */
101goog.async.run.workQueue_ = new goog.async.WorkQueue();
102
103
104if (goog.DEBUG) {
105 /**
106 * Reset the work queue.
107 * @private
108 */
109 goog.async.run.resetQueue_ = function() {
110 goog.async.run.workQueueScheduled_ = false;
111 goog.async.run.workQueue_ = new goog.async.WorkQueue();
112 };
113
114 // If there is a clock implemenation in use for testing
115 // and it is reset, reset the queue.
116 goog.testing.watchers.watchClockReset(goog.async.run.resetQueue_);
117}
118
119
120/**
121 * Run any pending goog.async.run work items. This function is not intended
122 * for general use, but for use by entry point handlers to run items ahead of
123 * goog.async.nextTick.
124 */
125goog.async.run.processWorkQueue = function() {
126 // NOTE: additional work queue items may be added while processing.
127 var item = null;
128 while (item = goog.async.run.workQueue_.remove()) {
129 try {
130 item.fn.call(item.scope);
131 } catch (e) {
132 goog.async.throwException(e);
133 }
134 goog.async.run.workQueue_.returnUnused(item);
135 }
136
137 // There are no more work items, allow processing to be scheduled again.
138 goog.async.run.workQueueScheduled_ = false;
139};