# 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 |
15 | goog.provide('goog.async.run');
16 |
17 | goog.require('goog.async.WorkQueue');
18 | goog.require('goog.async.nextTick');
19 | goog.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 | */
30 | goog.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 | */
48 | goog.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 | */
78 | goog.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 | */
92 | goog.async.run.schedule_;
93 |
94 |
95 | /** @private {boolean} */
96 | goog.async.run.workQueueScheduled_ = false;
97 |
98 |
99 | /** @private {!goog.async.WorkQueue} */
100 | goog.async.run.workQueue_ = new goog.async.WorkQueue();
101 |
102 |
103 | if (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 | */
119 | goog.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 | };