run.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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. goog.provide('goog.async.run');
  15. goog.require('goog.async.WorkQueue');
  16. goog.require('goog.async.nextTick');
  17. goog.require('goog.async.throwException');
  18. /**
  19. * Fires the provided callback just before the current callstack unwinds, or as
  20. * soon as possible after the current JS execution context.
  21. * @param {function(this:THIS)} callback
  22. * @param {THIS=} opt_context Object to use as the "this value" when calling
  23. * the provided function.
  24. * @template THIS
  25. */
  26. goog.async.run = function(callback, opt_context) {
  27. if (!goog.async.run.schedule_) {
  28. goog.async.run.initializeRunner_();
  29. }
  30. if (!goog.async.run.workQueueScheduled_) {
  31. // Nothing is currently scheduled, schedule it now.
  32. goog.async.run.schedule_();
  33. goog.async.run.workQueueScheduled_ = true;
  34. }
  35. goog.async.run.workQueue_.add(callback, opt_context);
  36. };
  37. /**
  38. * Initializes the function to use to process the work queue.
  39. * @private
  40. */
  41. goog.async.run.initializeRunner_ = function() {
  42. // If native Promises are available in the browser, just schedule the callback
  43. // on a fulfilled promise, which is specified to be async, but as fast as
  44. // possible. Use goog.global.Promise instead of just Promise because the
  45. // relevant externs may be missing, and don't alias it because this could
  46. // confuse the compiler into thinking the polyfill is required when it should
  47. // be treated as optional.
  48. if (String(goog.global.Promise).indexOf('[native code]') != -1) {
  49. var promise = goog.global.Promise.resolve(undefined);
  50. goog.async.run.schedule_ = function() {
  51. promise.then(goog.async.run.processWorkQueue);
  52. };
  53. } else {
  54. goog.async.run.schedule_ = function() {
  55. goog.async.nextTick(goog.async.run.processWorkQueue);
  56. };
  57. }
  58. };
  59. /**
  60. * Forces goog.async.run to use nextTick instead of Promise.
  61. *
  62. * This should only be done in unit tests. It's useful because MockClock
  63. * replaces nextTick, but not the browser Promise implementation, so it allows
  64. * Promise-based code to be tested with MockClock.
  65. *
  66. * However, we also want to run promises if the MockClock is no longer in
  67. * control so we schedule a backup "setTimeout" to the unmocked timeout if
  68. * provided.
  69. *
  70. * @param {function(function())=} opt_realSetTimeout
  71. */
  72. goog.async.run.forceNextTick = function(opt_realSetTimeout) {
  73. goog.async.run.schedule_ = function() {
  74. goog.async.nextTick(goog.async.run.processWorkQueue);
  75. if (opt_realSetTimeout) {
  76. opt_realSetTimeout(goog.async.run.processWorkQueue);
  77. }
  78. };
  79. };
  80. /**
  81. * The function used to schedule work asynchronousely.
  82. * @private {function()}
  83. */
  84. goog.async.run.schedule_;
  85. /** @private {boolean} */
  86. goog.async.run.workQueueScheduled_ = false;
  87. /** @private {!goog.async.WorkQueue} */
  88. goog.async.run.workQueue_ = new goog.async.WorkQueue();
  89. if (goog.DEBUG) {
  90. /**
  91. * Reset the work queue. Only available for tests in debug mode.
  92. */
  93. goog.async.run.resetQueue = function() {
  94. goog.async.run.workQueueScheduled_ = false;
  95. goog.async.run.workQueue_ = new goog.async.WorkQueue();
  96. };
  97. }
  98. /**
  99. * Run any pending goog.async.run work items. This function is not intended
  100. * for general use, but for use by entry point handlers to run items ahead of
  101. * goog.async.nextTick.
  102. */
  103. goog.async.run.processWorkQueue = function() {
  104. // NOTE: additional work queue items may be added while processing.
  105. var item = null;
  106. while (item = goog.async.run.workQueue_.remove()) {
  107. try {
  108. item.fn.call(item.scope);
  109. } catch (e) {
  110. goog.async.throwException(e);
  111. }
  112. goog.async.run.workQueue_.returnUnused(item);
  113. }
  114. // There are no more work items, allow processing to be scheduled again.
  115. goog.async.run.workQueueScheduled_ = false;
  116. };