在通过 setTimeout 或 Promise 阻止代码之前更新 DOM

Red*_*edu 5 javascript asynchronous settimeout promise es6-promise

我知道,当存在 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);
Run Code Online (Sandbox Code Playgroud)
<p id="result"></p>
Run Code Online (Sandbox Code Playgroud)

但是,如果我将 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
Run Code Online (Sandbox Code Playgroud)
<p id="result"></p>
Run Code Online (Sandbox Code Playgroud)

然而,因为我知道承诺也会带你进入“某种”异步时间线,所以我希望在不使用setTimeout黑客的情况下达到相同的效果。例如;

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)
Run Code Online (Sandbox Code Playgroud)
<p id="result"></p>
Run Code Online (Sandbox Code Playgroud)

我至少希望它能在 FF 中按预期运行,因为这篇完美的文章(https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)可惜没办法。

有什么办法可以用承诺来完成这项工作吗?

Ben*_*aum 3

Promise.prototype.then具有微任务语义。这意味着它必须等待同步代码运行,而不是异步代码运行——浏览器可能选择等待所有 JS 运行后再进行 DOM 更新。

一般来说,微任务意味着它必须等待其他 JS 运行,然后才能运行,然后将控制权交给非 JS 代码。

setTimeout具有宏任务语义。它作为 DOM API 的一部分运行,当回调运行时,非 js 代码已经有机会运行。当它运行时,浏览器已经运行了自己的代码,因此它们也处理事件和 DOM 更新。

一般来说,macrotask意味着它必须等待所有其他 JS 运行以及“事件循环滴答”——即:事件触发。

这也是NodeJS之间的区别setImmediatenextTick

直接回答你的问题:不。没有办法强制浏览器在 microtick 更新中运行 DOM 更新——虽然技术上并没有禁止这样做——但这将是“不礼貌的”。

对于长时间运行的 CPU 密集型操作 - 我可以建议使用Web Workers吗?