在通过 setTimeout 或 promise 阻止代码之前更新 DOM
Updating DOM before blocking code by setTimeout or 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);
<p id="result"></p>
但是,如果我通过 setTimeout
将 CPU 密集型代码转移到异步时间线,一切都很好,如以下代码片段所示。
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>
然而,由于我知道 promises 也会将您带到 "sort of" 异步时间轴,所以我希望在不使用 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>
由于这篇完美的文章 (https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/),我至少希望它达到 FF 中预期的 运行,可惜不行。
有什么方法可以用承诺来完成这项工作吗?
Promise.prototype
.then
具有 microtask 语义。这意味着它必须等待同步代码 运行 而不是异步代码 运行 - 浏览器可能会选择等待所有 JS 运行 然后再进行 DOM 更新。
一般microtask就是要等其他JS到运行,然后才能运行before 将控制权交给非 JS 代码。
setTimeout
具有 macrotask 语义。它 运行s 作为 DOM API 的一部分并且当回调 运行s 非 js 代码已经得到有机会 运行。浏览器在 运行 时已经 运行 自己的代码,因此它们也处理事件和 DOM 更新。
通常 macrotask 意味着它必须等待所有其他 JS 运行 以及 "event loop to tick" - 即:事件触发。
这也是the difference between setImmediate
and nextTick
in NodeJS。
直接回答你的问题:不。 无法强制浏览器在 microtick 更新中进行 运行 DOM 更新 - 虽然它技术上不禁止它这样做 - 它将是 "bad mannered".
对于长时间 运行ning CPU 绑定操作 - 我可以建议改为 Web Workers 吗?
问题是 promise,即使它异步运行,运行得太早。所以浏览器没有时间更新 DOM。这个问题不是特定于承诺的,我在使用延迟为 0 毫秒的 setTimeout
时看到相同的结果:
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
<p id="result"></p>
其实你要的好像是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);
<p id="result"></p>
但此时您可以单独使用 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));
<p id="result"></p>
最好的方法是将繁重的过程委托给网络工作者...
// 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) {
...
}
我知道当有 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>
但是,如果我通过 setTimeout
将 CPU 密集型代码转移到异步时间线,一切都很好,如以下代码片段所示。
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>
然而,由于我知道 promises 也会将您带到 "sort of" 异步时间轴,所以我希望在不使用 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>
由于这篇完美的文章 (https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/),我至少希望它达到 FF 中预期的 运行,可惜不行。
有什么方法可以用承诺来完成这项工作吗?
Promise.prototype
.then
具有 microtask 语义。这意味着它必须等待同步代码 运行 而不是异步代码 运行 - 浏览器可能会选择等待所有 JS 运行 然后再进行 DOM 更新。
一般microtask就是要等其他JS到运行,然后才能运行before 将控制权交给非 JS 代码。
setTimeout
具有 macrotask 语义。它 运行s 作为 DOM API 的一部分并且当回调 运行s 非 js 代码已经得到有机会 运行。浏览器在 运行 时已经 运行 自己的代码,因此它们也处理事件和 DOM 更新。
通常 macrotask 意味着它必须等待所有其他 JS 运行 以及 "event loop to tick" - 即:事件触发。
这也是the difference between setImmediate
and nextTick
in NodeJS。
直接回答你的问题:不。 无法强制浏览器在 microtick 更新中进行 运行 DOM 更新 - 虽然它技术上不禁止它这样做 - 它将是 "bad mannered".
对于长时间 运行ning CPU 绑定操作 - 我可以建议改为 Web Workers 吗?
问题是 promise,即使它异步运行,运行得太早。所以浏览器没有时间更新 DOM。这个问题不是特定于承诺的,我在使用延迟为 0 毫秒的 setTimeout
时看到相同的结果:
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
<p id="result"></p>
其实你要的好像是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);
<p id="result"></p>
但此时您可以单独使用 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));
<p id="result"></p>
最好的方法是将繁重的过程委托给网络工作者...
// 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) {
...
}