用于中断浏览器事件上繁重计算的模式
Pattern for interrupting heavy computation on browser event
我正在研究一个客户端模拟,该模拟可以动态进行后台计算和视图刷新。但是,由于模拟始终是实时的,因此CPU最终会在密集的用户输入和编辑期间执行大量不必要的工作。
我想要实现的是一种在用户事件上杀死整个序列的方法。
主应用中的预期使用情况:
var sequence = new Sequence(heavyFunc1, heavyFunc2, updateDom);
document.addEventListener("click", sequence.stop)
sequence.run() //all the heavy computation runs until told to stop
Web 辅助角色中的预期使用情况:
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
};
我环顾四周,可以想到以下工具和模式:
setTimout(fcn,0) || setImmediate(fcn) shim :将setTimeout(fcn,0)
的各个步骤包装在主脚本和工作线程中,以便在序列结束之前处理新事件。
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();
};
此示例适用于 setTimeout,但在 setImmediate 中失败,其中按键事件始终排在最后。
debounce :这是典型的答案,似乎不适用于我的情况。延迟和批处理用户输入将部分降低处理强度,但代价是处理时间更长。
承诺:我已经对工人结果使用了承诺,并且可以在步骤之间引入额外的承诺来中断序列和处理新事件。我尝试(但失败了)使用标志检查或Promise.race
.
带杀戮标志
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();
});
});
};
与承诺赛跑
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]);
});
};
问题终止事件序列的推荐模式是什么?
简而言之: 调用堆栈>> Promise>> Message Queue (events & setTimeout)
事实证明,这个问题的表述很糟糕,并且对特定用例过于具体。通常,对于立即返回的同步函数,将它们包装在 promise 序列中确实会将控制权移交给下一个函数,但仍优先于消息队列中的事件,即使是在创建 Promise 之前触发的事件。接受的答案并不总是有效。
在下面的代码段中,事件、承诺、同步调用最终按同步、承诺、事件顺序执行。 (需要本地承诺)
因此,让事件中断繁重计算的唯一方法是使用 setTimeout 暂存繁重的同步函数。
//prep
function log(m) {document.getElementsByTagName('pre')[0].innerHTML += m+'<br>'}
function syncDelay(ms) {
for (var tgt=Date.now()+ms; Date.now()<tgt;) Math.random();
}
function async1() {syncDelay(100); log('1. async1')}
window.addEventListener('message', function(e){log(e.data)});
function syncPr1() {syncDelay(100); log('3. syncPromise1')}
function syncPr2() {syncDelay(100); log('4. syncPromise2')}
function syncPr3() {syncDelay(100); log('5. syncPromise3')}
//sequence
setTimeout(async1,0);
window.postMessage('2. events get done last', '*');
Promise.resolve()
.then(syncPr1)
.then(syncPr2)
.then(syncPr3)
.catch(function(e){console.log(e)});
log('6. sync items get executed before next promise call');
<pre></pre>
- Javascript,访问一个主要对象模块模式中的每个对象
- 是否有任何snippet或jQuery插件可以列出easylist.txt模式匹配的DOM中的所有元素
- 试图在引导模式内动态生成图表,得到offsetWidth错误
- 根据元素和容器大小计算边距
- 从Rally获取一个特定的标记,以便计算另一个字段中的值
- 同位素库错误:未捕获错误无布局模式包装生产线8
- 在DOM中查找一个模式并替换它's的内容使用jquery
- 使用D3.js计算带有字母间距的文本长度
- 如何缩短MongoDB ObjectId并在Mongoose模式中使用它
- 使用CSS或JavaScript计算分页符的数量
- D3.js模式不适用于弧形或圆环图
- Webdriver.io pageObject模式-通过传递参数来定义元素选择器
- 模块模式和这个
- 什么是接口计算模式
- 用于中断浏览器事件上繁重计算的模式
- 在javascript显示模块模式中计算的淘汰设置
- 计算模式与Javascript
- JavaScript:如何计算一个反向RegExp模式来剥离与源模式匹配的字符
- 在Knockout JS中使用揭示原型模式的计算观察对象
- Ember.js是通用计算属性模式的简写