I'm working on a client side simulation that does on the fly background computation and view refresh. However, because the simulation is always live, the CPU ends up doing a lot of unnecessary work during intense user inputs and edits.
What I want to achieve is a way to kill the whole sequence on user event.
anticipated usage in the main app:
var sequence = new Sequence(heavyFunc1, heavyFunc2, updateDom);
document.addEventListener("click", sequence.stop)
sequence.run() //all the heavy computation runs until told to stop
anticipated usage in a web worker:
var sequence = new Sequence(heavyFunc1, heavyFunc2, self.postmessage);
self.onmessage = function(e) {
if (e.data === 'stop') msg = sequence.stop;
else msg = sequence.run(e.data); //resets and restarts
};
I've looked around and can think of the following tools and patterns:
setTimout(fcn,0) || setImmediate(fcn) shim : Wrap the individual steps setTimeout(fcn,0)
inside the sequences in both the main script and worker to processe new events before the end of the sequence.
killFlag = false;
window.addEventListener('keypress', function() {killFlag = true});
//this exampe works only with setTimeout, fails with setImmediate lib
var interruptibleSequence = function(tasks) {
var iterate = function() {
if (killFlag || !tasks.length) return;
tasks.shift()();
if (tasks.length) window.setTimeout(iterate,0);
};
iterate();
};
This example worked with setTimeout but failed with setImmediate where the keypress event always came in last.
debounce : This is the typical answer that does not seem to apply in my case. Delaying and batching user inputs would partly reduce the processing intensity at the expense of longer processing time.
Promises : I'm already using promises for worker results and could introduce additional promises between steps to interrupt the sequence and process new events. I've tried (and failed) using either flag checks or Promise.race
.
With kill flag
killFlag = false;
window.addEventListener('keypress', function() {killFlag = true});
//does not work. promises get priority and event is only triggered last
var interruptibleSequence = function(tasks) {
var seq = Promise.resolve();
tasks.forEach(function(task){
seq = seq.then(function() {
if (killFlag) return;
else task();
});
});
};
With Promise.race
var killTrigger;
var killPromise = new Promise(function(res,rej){killTrigger = rej});
window.addEventListener('keypress', killTrigger());
//does not work. promises get priority and event is only triggered last
var raceToFailure = function(tasks) {
var seq = Promise.resolve();
tasks.forEach(function(task){
seq = Promise.race([seq.then(task),killPromise]);
});
};
Question What would be the recommended pattern to kill a sequence on event?