我知道当存在CPU密集型代码时,不会发生任何直接的先前DOM更新。如
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
result.textContent = "I am done..!";
}
result.textContent = "Please remain..."; // we will never see this
blockFor(2000);
<p id="result"></p>
但是,如果我将CPU密集型代码转移到异步时间轴setTimeout
,那么它就像在下面的代码段中一样正常。
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
result.textContent = "I am done..!";
}
result.textContent = "Please remain..."; // now you see me
setTimeout(_ => blockFor(2000),15); // 15ms to be on the safe side
<p id="result"></p>
然而,因为我知道承诺也会带你到一个“类型”的asycnronous时间线,我期望在不使用setTimeout
hack的情况下达到相同的效果。如;
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
result.textContent = "I am done..!";
}
result.textContent = "Please remain..."; // not in Chrome not in FF
Promise.resolve(2000)
.then(blockFor)
<p id="result"></p>
我至少期望这会在FF中按预期运行,因为这篇完美的文章(https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)唉,没办法。
有没有办法用承诺完成这项工作?
答案 0 :(得分:3)
Promise.prototype
.then
具有 microtask 语义。这意味着它必须等待同步代码运行,但不能运行异步代码 - 浏览器可能会选择在进行DOM更新之前等待所有JS运行。
通常微任务意味着它必须等待其他JS运行,然后它才能在控制非JS代码之前运行。
setTimeout
具有 macrotask 语义。它作为DOM API 的一部分运行,当回调运行时,非js代码已经有机会运行。浏览器在运行时已经运行了自己的代码,因此它们也处理事件和DOM更新。
通常 macrotask 意味着它必须等待所有其他JS运行,并且还要“等待事件循环” - 即:要触发的事件。
这也是the difference between setImmediate
and nextTick
in NodeJS。
直接回答你的问题:不。没有办法强制浏览器在microtick更新中运行DOM更新 - 虽然技术上不禁止它这样做 - 它会是“不礼貌”。
对于长时间运行的CPU绑定操作 - 我可以建议使用Web Workers吗?
答案 1 :(得分:1)
问题在于承诺,即使它以异步方式运行,也会过早运行。因此浏览器没有时间更新DOM。这个问题不是特定于承诺的,当使用setTimeout
延迟0ms时,我看到相同的结果:
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
result.textContent = "I am done..!";
}
result.textContent = "Please remain..."; // we will never see this
setTimeout(_ => blockFor(2000), 0); // 0ms is not enough
&#13;
<p id="result"></p>
&#13;
事实上,你想要的似乎是requestAnimationFrame
:
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
result.textContent = "I am done..!";
}
result.textContent = "Please remain..."; // now you see me
new Promise(function(resolve) {
requestAnimationFrame(_ => resolve(2000));
}).then(blockFor);
&#13;
<p id="result"></p>
&#13;
但此时你可以单独使用requestAnimationFrame
,没有承诺。
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
result.textContent = "I am done..!";
}
result.textContent = "Please remain..."; // now you see me
requestAnimationFrame(_ => blockFor(2000));
&#13;
<p id="result"></p>
&#13;
答案 2 :(得分:0)
最好的方法是将繁重的过程委派给网络工作者...
// main thread
document.getElementById("result").addEventListener('click', handleClick);
const worker = new Worker('worker.js');
function handleClick(){
worker.onmessage = e => {
console.log('main', e.data.response)
this.textContent = e.data.response;
}
this.textContent = "Please remain...";
worker.postMessage({data: 2000});
}
// worker
self.addEventListener('message', e => {
const { data } = e.data;
console.log('worker', data);
function blockFor(dur){
var now = new Date().getTime();
while (new Date().getTime() < now + dur);
}
blockFor(data)
self.postMessage({ response: "I am done..!" });
});
// NOTE: perform this test on your app for browser compatibility
if (window.Worker) {
...
}