123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- /**
- Some credit for this helper goes to http://github.com/YuzuJS/setImmediate
- */
- import { root } from './root';
- export class ImmediateDefinition {
- constructor(root) {
- this.root = root;
- if (root.setImmediate && typeof root.setImmediate === 'function') {
- this.setImmediate = root.setImmediate.bind(root);
- this.clearImmediate = root.clearImmediate.bind(root);
- }
- else {
- this.nextHandle = 1;
- this.tasksByHandle = {};
- this.currentlyRunningATask = false;
- // Don't get fooled by e.g. browserify environments.
- if (this.canUseProcessNextTick()) {
- // For Node.js before 0.9
- this.setImmediate = this.createProcessNextTickSetImmediate();
- }
- else if (this.canUsePostMessage()) {
- // For non-IE10 modern browsers
- this.setImmediate = this.createPostMessageSetImmediate();
- }
- else if (this.canUseMessageChannel()) {
- // For web workers, where supported
- this.setImmediate = this.createMessageChannelSetImmediate();
- }
- else if (this.canUseReadyStateChange()) {
- // For IE 6–8
- this.setImmediate = this.createReadyStateChangeSetImmediate();
- }
- else {
- // For older browsers
- this.setImmediate = this.createSetTimeoutSetImmediate();
- }
- let ci = function clearImmediate(handle) {
- delete clearImmediate.instance.tasksByHandle[handle];
- };
- ci.instance = this;
- this.clearImmediate = ci;
- }
- }
- identify(o) {
- return this.root.Object.prototype.toString.call(o);
- }
- canUseProcessNextTick() {
- return this.identify(this.root.process) === '[object process]';
- }
- canUseMessageChannel() {
- return Boolean(this.root.MessageChannel);
- }
- canUseReadyStateChange() {
- const document = this.root.document;
- return Boolean(document && 'onreadystatechange' in document.createElement('script'));
- }
- canUsePostMessage() {
- const root = this.root;
- // The test against `importScripts` prevents this implementation from being installed inside a web worker,
- // where `root.postMessage` means something completely different and can't be used for this purpose.
- if (root.postMessage && !root.importScripts) {
- let postMessageIsAsynchronous = true;
- let oldOnMessage = root.onmessage;
- root.onmessage = function () {
- postMessageIsAsynchronous = false;
- };
- root.postMessage('', '*');
- root.onmessage = oldOnMessage;
- return postMessageIsAsynchronous;
- }
- return false;
- }
- // This function accepts the same arguments as setImmediate, but
- // returns a function that requires no arguments.
- partiallyApplied(handler, ...args) {
- let fn = function result() {
- const { handler, args } = result;
- if (typeof handler === 'function') {
- handler.apply(undefined, args);
- }
- else {
- (new Function('' + handler))();
- }
- };
- fn.handler = handler;
- fn.args = args;
- return fn;
- }
- addFromSetImmediateArguments(args) {
- this.tasksByHandle[this.nextHandle] = this.partiallyApplied.apply(undefined, args);
- return this.nextHandle++;
- }
- createProcessNextTickSetImmediate() {
- let fn = function setImmediate() {
- const { instance } = setImmediate;
- let handle = instance.addFromSetImmediateArguments(arguments);
- instance.root.process.nextTick(instance.partiallyApplied(instance.runIfPresent, handle));
- return handle;
- };
- fn.instance = this;
- return fn;
- }
- createPostMessageSetImmediate() {
- // Installs an event handler on `global` for the `message` event: see
- // * https://developer.mozilla.org/en/DOM/window.postMessage
- // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
- const root = this.root;
- let messagePrefix = 'setImmediate$' + root.Math.random() + '$';
- let onGlobalMessage = function globalMessageHandler(event) {
- const instance = globalMessageHandler.instance;
- if (event.source === root &&
- typeof event.data === 'string' &&
- event.data.indexOf(messagePrefix) === 0) {
- instance.runIfPresent(+event.data.slice(messagePrefix.length));
- }
- };
- onGlobalMessage.instance = this;
- root.addEventListener('message', onGlobalMessage, false);
- let fn = function setImmediate() {
- const { messagePrefix, instance } = setImmediate;
- let handle = instance.addFromSetImmediateArguments(arguments);
- instance.root.postMessage(messagePrefix + handle, '*');
- return handle;
- };
- fn.instance = this;
- fn.messagePrefix = messagePrefix;
- return fn;
- }
- runIfPresent(handle) {
- // From the spec: 'Wait until any invocations of this algorithm started before this one have completed.'
- // So if we're currently running a task, we'll need to delay this invocation.
- if (this.currentlyRunningATask) {
- // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
- // 'too much recursion' error.
- this.root.setTimeout(this.partiallyApplied(this.runIfPresent, handle), 0);
- }
- else {
- let task = this.tasksByHandle[handle];
- if (task) {
- this.currentlyRunningATask = true;
- try {
- task();
- }
- finally {
- this.clearImmediate(handle);
- this.currentlyRunningATask = false;
- }
- }
- }
- }
- createMessageChannelSetImmediate() {
- let channel = new this.root.MessageChannel();
- channel.port1.onmessage = (event) => {
- let handle = event.data;
- this.runIfPresent(handle);
- };
- let fn = function setImmediate() {
- const { channel, instance } = setImmediate;
- let handle = instance.addFromSetImmediateArguments(arguments);
- channel.port2.postMessage(handle);
- return handle;
- };
- fn.channel = channel;
- fn.instance = this;
- return fn;
- }
- createReadyStateChangeSetImmediate() {
- let fn = function setImmediate() {
- const instance = setImmediate.instance;
- const root = instance.root;
- const doc = root.document;
- const html = doc.documentElement;
- let handle = instance.addFromSetImmediateArguments(arguments);
- // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
- // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
- let script = doc.createElement('script');
- script.onreadystatechange = () => {
- instance.runIfPresent(handle);
- script.onreadystatechange = null;
- html.removeChild(script);
- script = null;
- };
- html.appendChild(script);
- return handle;
- };
- fn.instance = this;
- return fn;
- }
- createSetTimeoutSetImmediate() {
- let fn = function setImmediate() {
- const instance = setImmediate.instance;
- let handle = instance.addFromSetImmediateArguments(arguments);
- instance.root.setTimeout(instance.partiallyApplied(instance.runIfPresent, handle), 0);
- return handle;
- };
- fn.instance = this;
- return fn;
- }
- }
- export const Immediate = new ImmediateDefinition(root);
- //# sourceMappingURL=Immediate.js.map
|