Last active
November 25, 2021 17:12
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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