Files
polymer/lib/utils/async.html
2017-03-29 15:52:01 -07:00

203 lines
6.3 KiB
HTML

<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="boot.html">
<script>
(function() {
'use strict';
/** @typedef {{run: function(function(), number=):number, cancel: function(number)}} */
let AsyncInterface; // eslint-disable-line no-unused-vars
// Microtask implemented using Mutation Observer
let microtaskCurrHandle = 0;
let microtaskLastHandle = 0;
let microtaskCallbacks = [];
let microtaskNodeContent = 0;
let microtaskNode = document.createTextNode('');
new window.MutationObserver(microtaskFlush).observe(microtaskNode, {characterData: true});
function microtaskFlush() {
const len = microtaskCallbacks.length;
for (let i = 0; i < len; i++) {
let cb = microtaskCallbacks[i];
if (cb) {
try {
cb();
} catch (e) {
setTimeout(() => { throw e });
}
}
}
microtaskCallbacks.splice(0, len);
microtaskLastHandle += len;
}
/**
* Module that provides a number of strategies for enqueuing asynchronous
* tasks. Each sub-module provides a standard `run(fn)` interface that returns a
* handle, and a `cancel(handle)` interface for canceling async tasks before
* they run.
*
* @namespace
* @memberof Polymer
* @summary Module that provides a number of strategies for enqueuing asynchronous
* tasks.
*/
Polymer.Async = {
/**
* Async interface wrapper around `setTimeout`.
*
* @namespace
* @memberof Polymer.Async
* @summary Async interface wrapper around `setTimeout`.
*/
timeOut: {
/**
* Returns a sub-module with the async interface providing the provided
* delay.
*
* @memberof Polymer.Async.timeOut
* @param {number} delay Time to wait before calling callbacks in ms
* @return {AsyncInterface} An async timeout interface
*/
after(delay) {
return {
run(fn) { return setTimeout(fn, delay) },
cancel: window.clearTimeout.bind(window)
}
},
/**
* Enqueues a function called in the next task.
*
* @memberof Polymer.Async.timeOut
* @param {Function} fn Callback to run
* @return {number} Handle used for canceling task
*/
run: window.setTimeout.bind(window),
/**
* Cancels a previously enqueued `timeOut` callback.
*
* @memberof Polymer.Async.timeOut
* @param {number} handle Handle returned from `run` of callback to cancel
*/
cancel: window.clearTimeout.bind(window)
},
/**
* Async interface wrapper around `requestAnimationFrame`.
*
* @namespace
* @memberof Polymer.Async
* @summary Async interface wrapper around `requestAnimationFrame`.
*/
animationFrame: {
/**
* Enqueues a function called at `requestAnimationFrame` timing.
*
* @memberof Polymer.Async.timeOut
* @param {Function} fn Callback to run
* @return {number} Handle used for canceling task
*/
run: window.requestAnimationFrame.bind(window),
/**
* Cancels a previously enqueued `animationFrame` callback.
*
* @memberof Polymer.Async.timeOut
* @param {number} handle Handle returned from `run` of callback to cancel
*/
cancel: window.cancelAnimationFrame.bind(window)
},
/**
* Async interface wrapper around `requestIdleCallback`. Falls back to
* `setTimeout` on browsers that do not support `requestIdleCallback`.
*
* @namespace
* @memberof Polymer.Async
* @summary Async interface wrapper around `requestIdleCallback`.
*/
idlePeriod: {
/**
* Enqueues a function called at `requestIdleCallback` timing.
*
* @memberof Polymer.Async.timeOut
* @param {function(IdleDeadline)} fn Callback to run
* @return {number} Handle used for canceling task
*/
run(fn) {
return window.requestIdleCallback ?
window.requestIdleCallback(fn) :
window.setTimeout(fn, 16);
},
/**
* Cancels a previously enqueued `idlePeriod` callback.
*
* @memberof Polymer.Async.timeOut
* @param {number} handle Handle returned from `run` of callback to cancel
*/
cancel(timer) {
return window.cancelIdleCallback ?
window.cancelIdleCallback(timer) :
window.clearTimeout(timer);
}
},
/**
* Async interface for enqueueing callbacks that run at microtask timing.
*
* Note that microtask timing is achieved via a single `MutationObserver`,
* and thus callbacks enqueued with this API will all run in a single
* batch, and not interleaved with other microtasks such as promises.
* Promises are avoided as an implementation choice for the time being
* due to Safari bugs that cause Promises to lack microtask guarantees.
*
* @namespace
* @memberof Polymer.Async
* @summary Async interface for enqueueing callbacks that run at microtask
* timing.
*/
microTask: {
/**
* Enqueues a function called at microtask timing.
*
* @memberof Polymer.Async.timeOut
* @param {Function} fn Callback to run
*/
run(callback) {
microtaskNode.textContent = microtaskNodeContent++;
microtaskCallbacks.push(callback);
return microtaskCurrHandle++;
},
/**
* Cancels a previously enqueued `microTask` callback.
*
* @param {number} handle Handle returned from `run` of callback to cancel
*/
cancel(handle) {
const idx = handle - microtaskLastHandle;
if (idx >= 0) {
if (!microtaskCallbacks[idx]) {
throw new Error('invalid async handle: ' + handle);
}
microtaskCallbacks[idx] = null;
}
}
}
};
})();
</script>