Skip to content

Commit

Permalink
utils: refactor nexttick module.
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Jun 26, 2017
1 parent 055e811 commit edb5bb7
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 214 deletions.
252 changes: 249 additions & 3 deletions lib/utils/nexttick-browser.js
Original file line number Diff line number Diff line change
@@ -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 6–8.
nextTick = installReadyState();
} else if (hasSetTimeout()) {
// For older browsers.
nextTick = installSetTimeout();
} else {
throw new Error('nextTick not supported.');
}

/*
* Expose
*/

module.exports = nextTick;
6 changes: 3 additions & 3 deletions lib/utils/nexttick.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

'use strict';

module.exports = typeof setImmediate !== 'function'
? process.nextTick
: setImmediate;
module.exports = function nextTick(handler) {
setImmediate(handler);
};
Loading

0 comments on commit edb5bb7

Please sign in to comment.