Skip to content

Instantly share code, notes, and snippets.

@jpvincent
Last active November 25, 2021 17:12
  • Star 13 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save jpvincent/867010d224d61a6539d3 to your computer and use it in GitHub Desktop.
never blocking loop mechanism, using setTimeout 0 trick and time control to break the loop and let the browser breath
/**
* NEVER BLOCKING LOOP : implementation of the infamous setTimeout 0 hack, with time checking in order to guarantee fluidity without sacrificing execution speed.
*
* USAGE :
* var array = ["a way too big array that is heavy to process"]
* optimizeFor({
* nbIterations: array.length,
* each:function( index ) {
* doSomethingUsefulWith( array[ index ] );
* },
* end: function() {
* console.log('ended processing array as fast as possible without blocking the browser');
* }
* );
*
* OPTIONS :
* @nbIterations : number of loops to execute (or a function that returns true / false)
* @each : will be executed each time. iterationNumber will be given as an argument
* @end : will be called once loop ends
* @step : max number of milliseconds a loop can block the browser. step up = loop runs faster, browser is less fluid. 0 = max fluidity, task last longer
*
* DEPENDANCIES
* namespace: window by default, but you dont want globals right ?
* extend : jQuery OR underscorejs extend method
* noop : empty function, you can use $.noop
*/
(function (namespace, extend, noop) {
namespace.optimizedFor = function(options) {
// for IE10, or if you include a decent setImmediate polyfill (say https://github.com/YuzuJS/setImmediate)
var setImmediate = window.setImmediate || function(callback) {
setTimeout(callback, 0);
},
// native or polyfill way to have a number of milliseconds
now = Date.now || function() {
return Number(new Date);
}
;
// replace ourselves
namespace.optimizedFor = function optimizedFor(options) {
options = extend({
nbIterations: 0,
each: noop,
end: noop, //
step: 40 //
}, options);
var i = 0,
check;
// allow the user to provide its own stop method
if (typeof options.nbIterations === 'function') {
check = options.nbIterations;
} else {
// by default, just check we can still
check = function(i) {
return i < options.nbIterations;
};
}
(function mainLoop() {
//
for (var lastStart = now(); check(i); i++) {
options.each(i);
//
if ((now() - lastStart) > 40) {
// let the browser breath
setImmediate(mainLoop);
// break the loop but do not forget to increment
i++;
return;
}
}
// final method (must be async too)
setImmediate(options.end);
}());
};
namespace.optimizedFor(options);
};
}(window, $.extend, function(){}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment