Path: blob/trunk/third_party/closure/goog/async/nexttick.js
4104 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Provides a function to schedule running a function as soon8* as possible after the current JS execution stops and yields to the event9* loop.10*/1112goog.provide('goog.async.nextTick');1314goog.require('goog.debug.entryPointRegistry');15goog.require('goog.dom');16goog.require('goog.dom.TagName');17goog.require('goog.functions');18goog.require('goog.labs.userAgent.browser');19goog.require('goog.labs.userAgent.engine');202122/**23* Fires the provided callbacks as soon as possible after the current JS24* execution context. setTimeout(…, 0) takes at least 4ms when called from25* within another setTimeout(…, 0) for legacy reasons.26*27* This will not schedule the callback as a microtask (i.e. a task that can28* preempt user input or networking callbacks). It is meant to emulate what29* setTimeout(_, 0) would do if it were not throttled. If you desire microtask30* behavior, use {@see goog.Promise} instead.31*32* @param {function(this:SCOPE)} callback Callback function to fire as soon as33* possible.34* @param {SCOPE=} opt_context Object in whose scope to call the listener.35* @param {boolean=} opt_useSetImmediate Avoid the IE workaround that36* ensures correctness at the cost of speed. See comments for details.37* @template SCOPE38*/39goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) {40'use strict';41var cb = callback;42if (opt_context) {43cb = goog.bind(callback, opt_context);44}45cb = goog.async.nextTick.wrapCallback_(cb);46// Note we do allow callers to also request setImmediate if they are willing47// to accept the possible tradeoffs of incorrectness in exchange for speed.48// The IE fallback of readystate change is much slower. See useSetImmediate_49// for details.50if (typeof goog.global.setImmediate === 'function' &&51(opt_useSetImmediate || goog.async.nextTick.useSetImmediate_())) {52goog.global.setImmediate(cb);53return;54}5556// Look for and cache the custom fallback version of setImmediate.57if (!goog.async.nextTick.nextTickImpl) {58goog.async.nextTick.nextTickImpl = goog.async.nextTick.getNextTickImpl_();59}60goog.async.nextTick.nextTickImpl(cb);61};626364/**65* Returns whether should use setImmediate implementation currently on window.66*67* window.setImmediate was introduced and currently only supported by IE10+,68* but due to a bug in the implementation it is not guaranteed that69* setImmediate is faster than setTimeout nor that setImmediate N is before70* setImmediate N+1. That is why we do not use the native version if71* available. We do, however, call setImmediate if it is a non-native function72* because that indicates that it has been replaced by goog.testing.MockClock73* which we do want to support.74* See75* http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie1076*77* @return {boolean} Whether to use the implementation of setImmediate defined78* on Window.79* @private80* @suppress {missingProperties} For "Window.prototype.setImmediate"81*/82goog.async.nextTick.useSetImmediate_ = function() {83'use strict';84// Not a browser environment.85if (!goog.global.Window || !goog.global.Window.prototype) {86return true;87}8889// MS Edge has window.setImmediate natively, but it's not on Window.prototype.90// Also, there's no clean way to detect if the goog.global.setImmediate has91// been replaced by mockClock as its replacement also shows up as "[native92// code]" when using toString. Therefore, just always use93// goog.global.setImmediate for Edge. It's unclear if it suffers the same94// issues as IE10/11, but based on95// https://dev.modern.ie/testdrive/demos/setimmediatesorting/96// it seems they've been working to ensure it's WAI.97if (goog.labs.userAgent.browser.isEdge() ||98goog.global.Window.prototype.setImmediate != goog.global.setImmediate) {99// Something redefined setImmediate in which case we decide to use it (This100// is so that we use the mockClock setImmediate).101return true;102}103104return false;105};106107108/**109* Cache for the nextTick implementation. Exposed so tests can replace it,110* if needed.111* @type {function(function())}112*/113goog.async.nextTick.nextTickImpl;114115116/**117* Determines the best possible implementation to run a function as soon as118* the JS event loop is idle.119* @return {function(function())} The "setImmediate" implementation.120* @private121*/122goog.async.nextTick.getNextTickImpl_ = function() {123'use strict';124// Create a private message channel and use it to postMessage empty messages125// to ourselves.126/** @type {!Function|undefined} */127var Channel = goog.global['MessageChannel'];128// If MessageChannel is not available and we are in a browser, implement129// an iframe based polyfill in browsers that have postMessage and130// document.addEventListener. The latter excludes IE8 because it has a131// synchronous postMessage implementation.132if (typeof Channel === 'undefined' && typeof window !== 'undefined' &&133window.postMessage && window.addEventListener &&134// Presto (The old pre-blink Opera engine) has problems with iframes135// and contentWindow.136!goog.labs.userAgent.engine.isPresto()) {137/** @constructor */138Channel = function() {139'use strict';140// Make an empty, invisible iframe.141var iframe = goog.dom.createElement(goog.dom.TagName.IFRAME);142iframe.style.display = 'none';143document.documentElement.appendChild(iframe);144var win = iframe.contentWindow;145var doc = win.document;146doc.open();147doc.close();148// Do not post anything sensitive over this channel, as the workaround for149// pages with file: origin could allow that information to be modified or150// intercepted.151var message = 'callImmediate' + Math.random();152// The same origin policy rejects attempts to postMessage from file: urls153// unless the origin is '*'.154var origin = win.location.protocol == 'file:' ?155'*' :156win.location.protocol + '//' + win.location.host;157var onmessage = goog.bind(function(e) {158'use strict';159// Validate origin and message to make sure that this message was160// intended for us. If the origin is set to '*' (see above) only the161// message needs to match since, for example, '*' != 'file://'. Allowing162// the wildcard is ok, as we are not concerned with security here.163if ((origin != '*' && e.origin != origin) || e.data != message) {164return;165}166this['port1'].onmessage();167}, this);168win.addEventListener('message', onmessage, false);169this['port1'] = {};170this['port2'] = {171postMessage: function() {172'use strict';173win.postMessage(message, origin);174}175};176};177}178if (typeof Channel !== 'undefined' && !goog.labs.userAgent.browser.isIE()) {179// Exclude all of IE due to180// http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/181// which allows starving postMessage with a busy setTimeout loop.182// This currently affects IE10 and IE11 which would otherwise be able183// to use the postMessage based fallbacks.184var channel = new Channel();185// Use a fifo linked list to call callbacks in the right order.186var head = {};187var tail = head;188channel['port1'].onmessage = function() {189'use strict';190if (head.next !== undefined) {191head = head.next;192var cb = head.cb;193head.cb = null;194cb();195}196};197return function(cb) {198'use strict';199tail.next = {cb: cb};200tail = tail.next;201channel['port2'].postMessage(0);202};203}204// Fall back to setTimeout with 0. In browsers this creates a delay of 5ms205// or more.206// NOTE(user): This fallback is used for IE.207return function(cb) {208'use strict';209goog.global.setTimeout(/** @type {function()} */ (cb), 0);210};211};212213214/**215* Helper function that is overrided to protect callbacks with entry point216* monitor if the application monitors entry points.217* @param {function()} callback Callback function to fire as soon as possible.218* @return {function()} The wrapped callback.219* @private220*/221goog.async.nextTick.wrapCallback_ = goog.functions.identity;222223224// Register the callback function as an entry point, so that it can be225// monitored for exception handling, etc. This has to be done in this file226// since it requires special code to handle all browsers.227goog.debug.entryPointRegistry.register(228/**229* @param {function(!Function): !Function} transformer The transforming230* function.231*/232function(transformer) {233'use strict';234goog.async.nextTick.wrapCallback_ = transformer;235});236237238