259 lines
5.6 KiB
JavaScript
259 lines
5.6 KiB
JavaScript
/*!
|
||
* 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';
|
||
|
||
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) {
|
||
if (typeof handler !== 'function')
|
||
throw new Error('callback must be a function.');
|
||
|
||
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 6–8.
|
||
nextTick = installReadyState();
|
||
} else if (hasSetTimeout()) {
|
||
// For older browsers.
|
||
nextTick = installSetTimeout();
|
||
} else {
|
||
throw new Error('nextTick not supported.');
|
||
}
|
||
|
||
/*
|
||
* Expose
|
||
*/
|
||
|
||
module.exports = nextTick;
|