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