You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
8.9 KiB
209 lines
8.9 KiB
/** |
|
Some credit for this helper goes to http://github.com/YuzuJS/setImmediate |
|
*/ |
|
"use strict"; |
|
var root_1 = require('./root'); |
|
var ImmediateDefinition = (function () { |
|
function ImmediateDefinition(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(); |
|
} |
|
var ci = function clearImmediate(handle) { |
|
delete clearImmediate.instance.tasksByHandle[handle]; |
|
}; |
|
ci.instance = this; |
|
this.clearImmediate = ci; |
|
} |
|
} |
|
ImmediateDefinition.prototype.identify = function (o) { |
|
return this.root.Object.prototype.toString.call(o); |
|
}; |
|
ImmediateDefinition.prototype.canUseProcessNextTick = function () { |
|
return this.identify(this.root.process) === '[object process]'; |
|
}; |
|
ImmediateDefinition.prototype.canUseMessageChannel = function () { |
|
return Boolean(this.root.MessageChannel); |
|
}; |
|
ImmediateDefinition.prototype.canUseReadyStateChange = function () { |
|
var document = this.root.document; |
|
return Boolean(document && 'onreadystatechange' in document.createElement('script')); |
|
}; |
|
ImmediateDefinition.prototype.canUsePostMessage = function () { |
|
var 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) { |
|
var postMessageIsAsynchronous_1 = true; |
|
var oldOnMessage = root.onmessage; |
|
root.onmessage = function () { |
|
postMessageIsAsynchronous_1 = false; |
|
}; |
|
root.postMessage('', '*'); |
|
root.onmessage = oldOnMessage; |
|
return postMessageIsAsynchronous_1; |
|
} |
|
return false; |
|
}; |
|
// This function accepts the same arguments as setImmediate, but |
|
// returns a function that requires no arguments. |
|
ImmediateDefinition.prototype.partiallyApplied = function (handler) { |
|
var args = []; |
|
for (var _i = 1; _i < arguments.length; _i++) { |
|
args[_i - 1] = arguments[_i]; |
|
} |
|
var fn = function result() { |
|
var _a = result, handler = _a.handler, args = _a.args; |
|
if (typeof handler === 'function') { |
|
handler.apply(undefined, args); |
|
} |
|
else { |
|
(new Function('' + handler))(); |
|
} |
|
}; |
|
fn.handler = handler; |
|
fn.args = args; |
|
return fn; |
|
}; |
|
ImmediateDefinition.prototype.addFromSetImmediateArguments = function (args) { |
|
this.tasksByHandle[this.nextHandle] = this.partiallyApplied.apply(undefined, args); |
|
return this.nextHandle++; |
|
}; |
|
ImmediateDefinition.prototype.createProcessNextTickSetImmediate = function () { |
|
var fn = function setImmediate() { |
|
var instance = setImmediate.instance; |
|
var handle = instance.addFromSetImmediateArguments(arguments); |
|
instance.root.process.nextTick(instance.partiallyApplied(instance.runIfPresent, handle)); |
|
return handle; |
|
}; |
|
fn.instance = this; |
|
return fn; |
|
}; |
|
ImmediateDefinition.prototype.createPostMessageSetImmediate = function () { |
|
// 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 |
|
var root = this.root; |
|
var messagePrefix = 'setImmediate$' + root.Math.random() + '$'; |
|
var onGlobalMessage = function globalMessageHandler(event) { |
|
var 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); |
|
var fn = function setImmediate() { |
|
var _a = setImmediate, messagePrefix = _a.messagePrefix, instance = _a.instance; |
|
var handle = instance.addFromSetImmediateArguments(arguments); |
|
instance.root.postMessage(messagePrefix + handle, '*'); |
|
return handle; |
|
}; |
|
fn.instance = this; |
|
fn.messagePrefix = messagePrefix; |
|
return fn; |
|
}; |
|
ImmediateDefinition.prototype.runIfPresent = function (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 { |
|
var task = this.tasksByHandle[handle]; |
|
if (task) { |
|
this.currentlyRunningATask = true; |
|
try { |
|
task(); |
|
} |
|
finally { |
|
this.clearImmediate(handle); |
|
this.currentlyRunningATask = false; |
|
} |
|
} |
|
} |
|
}; |
|
ImmediateDefinition.prototype.createMessageChannelSetImmediate = function () { |
|
var _this = this; |
|
var channel = new this.root.MessageChannel(); |
|
channel.port1.onmessage = function (event) { |
|
var handle = event.data; |
|
_this.runIfPresent(handle); |
|
}; |
|
var fn = function setImmediate() { |
|
var _a = setImmediate, channel = _a.channel, instance = _a.instance; |
|
var handle = instance.addFromSetImmediateArguments(arguments); |
|
channel.port2.postMessage(handle); |
|
return handle; |
|
}; |
|
fn.channel = channel; |
|
fn.instance = this; |
|
return fn; |
|
}; |
|
ImmediateDefinition.prototype.createReadyStateChangeSetImmediate = function () { |
|
var fn = function setImmediate() { |
|
var instance = setImmediate.instance; |
|
var root = instance.root; |
|
var doc = root.document; |
|
var html = doc.documentElement; |
|
var 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. |
|
var script = doc.createElement('script'); |
|
script.onreadystatechange = function () { |
|
instance.runIfPresent(handle); |
|
script.onreadystatechange = null; |
|
html.removeChild(script); |
|
script = null; |
|
}; |
|
html.appendChild(script); |
|
return handle; |
|
}; |
|
fn.instance = this; |
|
return fn; |
|
}; |
|
ImmediateDefinition.prototype.createSetTimeoutSetImmediate = function () { |
|
var fn = function setImmediate() { |
|
var instance = setImmediate.instance; |
|
var handle = instance.addFromSetImmediateArguments(arguments); |
|
instance.root.setTimeout(instance.partiallyApplied(instance.runIfPresent, handle), 0); |
|
return handle; |
|
}; |
|
fn.instance = this; |
|
return fn; |
|
}; |
|
return ImmediateDefinition; |
|
}()); |
|
exports.ImmediateDefinition = ImmediateDefinition; |
|
exports.Immediate = new ImmediateDefinition(root_1.root); |
|
//# sourceMappingURL=Immediate.js.map
|