utils: refactor nexttick module.

This commit is contained in:
Christopher Jeffrey 2017-06-26 03:35:21 -07:00
parent 055e811333
commit edb5bb73af
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 252 additions and 214 deletions

View File

@ -1,9 +1,255 @@
/*!
* nexttick.js - setimmediate for bcoin
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* nexttick.js - nexttick for bcoin
* Copyright (c) 2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*
* Parts of this software are based on setimmediate.
*
* Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 'Software'), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
'use strict';
module.exports = require('../../vendor/setimmediate');
var global = (function() {
if (this)
return this;
if (typeof window !== 'undefined')
return window;
if (typeof self !== 'undefined')
return self;
if (typeof global !== 'undefined')
return global;
throw new Error('No global defined.');
})();
var document = global.document;
var nextHandle = 1;
var taskMap = {};
var running = false;
var nextTick;
/*
* Task Runner
*/
function addTask(handler) {
if (typeof handler !== 'function')
throw new Error('callback must be a function.');
taskMap[nextHandle] = handler;
return nextHandle++;
}
function runTask(handle) {
var task;
if (running) {
setTimeout(function() {
runTask(handle);
}, 1);
return;
}
task = taskMap[handle];
if (task) {
running = true;
try {
task();
} finally {
delete taskMap[handle];
running = false;
}
}
}
/*
* Set Immediate Implementation
*/
function hasSetImmediate() {
return typeof global.setImmediate === 'function';
}
function installSetImmediate() {
return function nextTick(handler) {
setImmediate(handler);
};
}
/*
* Next Tick Implementation
*/
function hasNextTick() {
// Don't get fooled by browserify.
return ({}).toString.call(global.process) === '[object process]';
}
function installNextTick() {
return process.nextTick;
}
/*
* Post Message Implementation
*/
function hasPostMessage() {
var isAsync = false;
var onMessage;
// Be sure to exclude web workers.
if (global.postMessage && !global.importScripts) {
isAsync = true;
onMessage = global.onmessage;
global.onmessage = function() {
isAsync = false;
};
global.postMessage('', '*');
global.onmessage = onMessage;
}
return isAsync;
}
function installPostMessage() {
var prefix = 'nextTick' + Math.random();
var onMessage;
onMessage = function(event) {
if (event.source === global
&& typeof event.data === 'string'
&& event.data.indexOf(prefix) === 0) {
runTask(+event.data.slice(prefix.length));
}
};
if (global.addEventListener)
global.addEventListener('message', onMessage, false);
else
global.attachEvent('onmessage', onMessage);
return function nextTick(handler) {
var handle = addTask(handler);
global.postMessage(prefix + handle, '*');
};
}
/*
* Message Channel Implementation
*/
function hasMessageChannel() {
return typeof global.MessageChannel === 'function';
}
function installMessageChannel() {
var channel = new MessageChannel();
channel.port1.onmessage = function(event) {
runTask(event.data);
};
return function nextTick(handler) {
var handle = addTask(handler);
channel.port2.postMessage(handle);
};
}
/*
* Ready State Change Implementation
*/
function hasReadyState() {
return document && ('onreadystatechange' in document.createElement('script'));
}
function installReadyState() {
var html = document.documentElement;
return function nextTick(handler) {
var handle = addTask(handler);
var script = document.createElement('script');
script.onreadystatechange = function() {
runTask(handle);
script.onreadystatechange = null;
html.removeChild(script);
script = null;
};
html.appendChild(script);
};
}
/*
* Set Timeout Implementation
*/
function hasSetTimeout() {
return typeof global.setTimeout === 'function';
}
function installSetTimeout() {
return function nextTick(handler) {
var handle = addTask(handler);
setTimeout(function() {
runTask(handle);
}, 1);
};
}
/*
* Install
*/
if (hasSetImmediate()) {
// `setImmediate` is already available.
nextTick = installSetImmediate();
} else if (hasNextTick()) {
// For Node.js before 0.9.
nextTick = installNextTick();
} else if (hasPostMessage()) {
// For non-IE10 modern browsers.
nextTick = installPostMessage();
} else if (hasMessageChannel()) {
// For web workers, where supported.
nextTick = installMessageChannel();
} else if (hasReadyState()) {
// For IE 68.
nextTick = installReadyState();
} else if (hasSetTimeout()) {
// For older browsers.
nextTick = installSetTimeout();
} else {
throw new Error('nextTick not supported.');
}
/*
* Expose
*/
module.exports = nextTick;

View File

@ -6,6 +6,6 @@
'use strict';
module.exports = typeof setImmediate !== 'function'
? process.nextTick
: setImmediate;
module.exports = function nextTick(handler) {
setImmediate(handler);
};

208
vendor/setimmediate.js vendored
View File

@ -1,208 +0,0 @@
/*!
* Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
(function(root, undefined) {
"use strict";
var global;
if (typeof window !== "undefined")
global = window;
else if (typeof self !== "undefined")
global = self;
else
global = root;
if (!global)
throw new Error("Global not found.");
function install() {
if (global.setImmediate) {
return global.setImmediate;
}
var nextHandle = 1; // Spec says greater than zero
var tasksByHandle = {};
var currentlyRunningATask = false;
var doc = global.document;
var setImmediate;
function addFromSetImmediateArguments(args) {
tasksByHandle[nextHandle] = partiallyApplied.apply(undefined, args);
return nextHandle++;
}
// This function accepts the same arguments as setImmediate, but
// returns a function that requires no arguments.
function partiallyApplied(handler) {
var args = [].slice.call(arguments, 1);
return function() {
if (typeof handler === "function") {
handler.apply(undefined, args);
} else {
(new Function("" + handler))();
}
};
}
function 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 (currentlyRunningATask) {
// Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
// "too much recursion" error.
setTimeout(partiallyApplied(runIfPresent, handle), 0);
} else {
var task = tasksByHandle[handle];
if (task) {
currentlyRunningATask = true;
try {
task();
} finally {
clearImmediate(handle);
currentlyRunningATask = false;
}
}
}
}
function clearImmediate(handle) {
delete tasksByHandle[handle];
}
function installNextTickImplementation() {
setImmediate = function() {
var handle = addFromSetImmediateArguments(arguments);
process.nextTick(partiallyApplied(runIfPresent, handle));
return handle;
};
}
function canUsePostMessage() {
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
// where `global.postMessage` means something completely different and can't be used for this purpose.
if (global.postMessage && !global.importScripts) {
var postMessageIsAsynchronous = true;
var oldOnMessage = global.onmessage;
global.onmessage = function() {
postMessageIsAsynchronous = false;
};
global.postMessage("", "*");
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
}
}
function installPostMessageImplementation() {
// 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 messagePrefix = "setImmediate$" + Math.random() + "$";
var onGlobalMessage = function(event) {
if (event.source === global &&
typeof event.data === "string" &&
event.data.indexOf(messagePrefix) === 0) {
runIfPresent(+event.data.slice(messagePrefix.length));
}
};
if (global.addEventListener) {
global.addEventListener("message", onGlobalMessage, false);
} else {
global.attachEvent("onmessage", onGlobalMessage);
}
setImmediate = function() {
var handle = addFromSetImmediateArguments(arguments);
global.postMessage(messagePrefix + handle, "*");
return handle;
};
}
function installMessageChannelImplementation() {
var channel = new MessageChannel();
channel.port1.onmessage = function(event) {
var handle = event.data;
runIfPresent(handle);
};
setImmediate = function() {
var handle = addFromSetImmediateArguments(arguments);
channel.port2.postMessage(handle);
return handle;
};
}
function installReadyStateChangeImplementation() {
var html = doc.documentElement;
setImmediate = function() {
var handle = 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 () {
runIfPresent(handle);
script.onreadystatechange = null;
html.removeChild(script);
script = null;
};
html.appendChild(script);
return handle;
};
}
function installSetTimeoutImplementation() {
setImmediate = function() {
var handle = addFromSetImmediateArguments(arguments);
setTimeout(partiallyApplied(runIfPresent, handle), 0);
return handle;
};
}
// Don't get fooled by e.g. browserify environments.
if ({}.toString.call(global.process) === "[object process]") {
// For Node.js before 0.9
installNextTickImplementation();
} else if (canUsePostMessage()) {
// For non-IE10 modern browsers
installPostMessageImplementation();
} else if (global.MessageChannel) {
// For web workers, where supported
installMessageChannelImplementation();
} else if (doc && "onreadystatechange" in doc.createElement("script")) {
// For IE 68
installReadyStateChangeImplementation();
} else {
// For older browsers
installSetTimeoutImplementation();
}
return setImmediate;
}
module.exports = install();
})(this);