Skip to content

Instantly share code, notes, and snippets.

@bluejava bluejava/Soon
Last active Apr 28, 2019

Embed
What would you like to do?
A Very Fast Javascript thread yield (see blog posting)
// See http://www.bluejava.com/4NS/Speed-up-your-Websites-with-a-Faster-setTimeout-using-soon
// This is a very fast "asynchronous" flow control - i.e. it yields the thread and executes later,
// but not much later. It is far faster and lighter than using setTimeout(fn,0) for yielding threads.
// Its also faster than other setImmediate shims, as it uses Mutation Observer and "mainlines" successive
// calls internally.
// WARNING: This does not yield to the browser UI loop, so by using this repeatedly
// you can starve the UI and be unresponsive to the user.
// Note: For an even faster version, see https://gist.github.com/bluejava/b3eb39911da03a740727
var soon = (function() {
var fq = []; // function queue;
function callQueue()
{
while(fq.length) // this approach allows new yields to pile on during the execution of these
{
var fe = fq[0];
fe.f.apply(fe.m,fe.a) // call our fn with the args and preserve context
fq.shift(); // remove element just processed... do this after processing so we don't go 0 and trigger soon again
}
}
// run the callQueue function asyncrhonously, as fast as possible
var cqYield = (function() {
// This is the fastest way browsers have to yield processing
if(typeof MutationObserver !== "undefined")
{
// first, create a div not attached to DOM to "observe"
var dd = document.createElement("div");
var mo = new MutationObserver(callQueue);
mo.observe(dd, { attributes: true });
return function(fn) { dd.setAttribute("a",0); } // trigger callback to
}
// if No MutationObserver - this is the next best thing - handles Node and MSIE
if(typeof setImmediate !== "undefined")
return function() { setImmediate(callQueue) }
// final fallback - shouldn't be used for much except very old browsers
return function() { setTimeout(callQueue,0) }
})();
// this is the function that will be assigned to soon
// it takes the function to call and examines all arguments
return function(fn) {
// push the function and any remaining arguments along with context
fq.push({f:fn,a:[].slice.apply(arguments).splice(1),m:this});
if(fq.length == 1) // upon adding our first entry, kick off the callback
cqYield();
};
})();
@bluejava

This comment has been minimized.

Copy link
Owner Author

commented Jan 18, 2015

I've made a couple optimizations which make it a bit faster. This currently can loop a million times in about 2500ms on Firefox v35 on a 5 year old iMac. Removing ability to pass context and arguments can speed it up even faster - I have a mind-numbingly fast version that executes 1 million loops in about 100ms - will post as separate gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.