Skip to content

Instantly share code, notes, and snippets.

@treelite
Last active December 29, 2015 04:09
Show Gist options
  • Save treelite/7612977 to your computer and use it in GitHub Desktop.
Save treelite/7612977 to your computer and use it in GitHub Desktop.
nextTick
/**
* @file nextTick
* @author treelite(c.xinle@gmail.com)
*/
define(function () {
var res;
var Observer;
var callbacks = [];
var attributeName = 'promise';
function handler(mutations) {
var item = mutations[0];
if (item.attributeName == attributeName) {
var len = callbacks.length;
for (var i = 0; i < len; i++) {
callbacks[i]();
}
callbacks.splice(0, i);
}
}
// for node or IE 10
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html#processingmodel
if (typeof setImmediate == 'function') {
res = setImmediate;
}
// for modern browser
// http://caniuse.com/#search=mutationobserver
else if (Observer = window.MutationObserver
|| window.webKitMutationObserver
) {
var observer = new Observer(handler);
var ele = document.createElement('div');
observer.observe(ele, {attributes: true});
res = function (fn) {
callbacks.push(fn);
ele.setAttribute(
attributeName,
Date.now ? Date.now() : (new Date()).getTime()
);
};
}
// for node older version
else if (typeof process == 'object' && process.nextTick) {
res = process.nextTick;
}
// for older browser
else {
res = function (fn) {
setTimeout(fn, 0);
};
}
return res;
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
<title>NextTick - Performance Test</title>
<style>
#logger {
border: 1px solid #CCC;
padding: 5px;
}
#logger p {
border-bottom: 1px solid #CCC;
}
</style>
</head>
<body>
<div id="logger"></div>
<script>
function blank () {}
function log(text) {
var ele = document.getElementById('logger');
var p = document.createElement('p');
p.innerHTML = text;
ele.appendChild(p);
}
function callback(list) {
var len = list.length;
for (var i = 0; i < len; i++) {
list[i]();
}
list.splice(0, len);
}
var nextTick = (function () {
var callbacks = [];
var attributeName = 'x';
var Observer = window.MutationObserver
|| window.webKitMutationObserver;
if (!Observer) {
return blank;
}
var observer = new Observer(function (mutations) {
var item = mutations[0];
if (item.attributeName == attributeName) {
callback(callbacks) ;
}
});
var ele = document.createElement('div');
observer.observe(ele, {attributes: true});
return function (fn) {
callbacks.push(fn);
ele.setAttribute(attributeName, Date.now());
};
})();
var setZeroTimeout = (function () {
var messageName = 'timeout';
var callbacks = [];
window.addEventListener(
'message',
function (e) {
if (e.source == window
&& e.data == messageName
) {
callback(callbacks);
}
},
false
);
return function (fn) {
callbacks.push(fn);
window.postMessage(messageName, '*');
};
})();
var messageChannel = (function () {
var callbacks = [];
if (typeof window.MessageChannel !== 'function') {
return blank;
}
var channel = new MessageChannel();
channel.port2.onmessage = function () {
callback(callbacks);
};
return function (fn) {
callbacks.push(fn);
channel.port1.postMessage('*');
};
})();
var max = 1000;
var sum;
var last;
function test(nextTick, index) {
last = Date.now();
nextTick(function () {
var now = Date.now();
sum += now - last;
if (++index < max) {
test(nextTick, index);
}
else {
log('sum: ' + sum);
log('repeat: ' + max);
log('avg: ' + sum / max);
runNextSpec();
}
});
}
var testIndex = 0;
var speces = [];
function addSpec(spec) {
if (spec.detect()) {
speces.push(spec);
}
else {
log('this browser do not support ' + spec.name);
}
}
function runNextSpec() {
var spec = speces[testIndex++];
if (!spec) {
return;
}
log('test ' + spec.name + ' ...');
sum = 0;
index = 0;
test(spec.nextTick, 0);
}
addSpec({
name: 'requestAnimationFrame',
detect: function () { return typeof window.requestAnimationFrame == 'function'; },
nextTick: function (fn) { window.requestAnimationFrame(fn); }
});
addSpec({
name: 'setTimeout',
detect: function () { return true; },
nextTick: function (fn) { setTimeout(fn); }
});
addSpec({
name: 'postMessage',
detect: function () { return typeof window.postMessage == 'function'; },
nextTick: setZeroTimeout
});
addSpec({
name: 'MessageChannel',
detect: function () { return typeof window.MessageChannel == 'function'; },
nextTick: messageChannel
});
addSpec({
name: 'MutationObserver',
detect: function () { return !!(window.MutationObserver || window.webKitMutationObserver) },
nextTick: nextTick
});
runNextSpec();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment