事件执行顺序和渲染
Event execution sequence and rendering
var con = document.getElementById('con');
con.onclick = function () {
Promise.resolve().then(function Promise1() {
con.textContent = 0;
// requestAnimationFrame(() => con.textContent = 0)
});
};
<div id="con">this is con</div>
为什么这段代码执行微任务后不触发渲染?
setTimeout(function setTimeout1() {
console.log('setTimeout1')
}, 0)
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1() {
console.log('postMessage');
Promise.resolve().then(function promise1() {
console.log('promise1');
})
};
channel.port2.postMessage(0);
setTimeout(function setTimeout2() {
console.log('setTimeout2')
}, 0);
console.log('sync');
为什么postmessage先于timer执行?
Why this code does not trigger rendering after performing microtasks?
确实如此,否则您将看不到正在更新的文本...
也许您无法从您的开发工具中分辨出来?
这 可能 因为鼠标事件现在通常被限制为屏幕刷新率,这意味着当调度鼠标事件的任务将 运行,你' d 已经在画框中,这可能是出于其他原因(因为据我所知,mousemove 事件以这种方式被限制,而不是 click ...).
因此,您的 Promise callback will get executed synchronously (with only the sixth step "set currentTask to null" in between), before the update the rendering 步骤开始,所有开发工具将看到一个正常的绘画框架,就像它预期的那样。
所以可能,开发工具不会在这里显示任何特别的东西,但考虑到你的主张的广泛性,很难确定一个具体原因,这只是我的理论。
您可以尝试通过从此类事件内部调用 requestAnimationFrame
来验证该理论,并检查它是否确实在同一事件循环迭代中执行:
onclick = (evt) => {
console.clear();
setTimeout( () => console.log( 'timeout' ), 0 );
requestAnimationFrame( () => console.log( 'rAF' ) );
};
Click anywhere<br>
If "rAF" gets logged before "timeout", the click event got handled in a painting frame.
对我来说,它在 Chrome 中经常出现,而在 Firefox 中只是偶尔出现一次,但同时我知道 Chrome's rAF is broken... 所以这个理论非常薄弱。
Why postmessage is executed before timer?
这将取决于用户代理(浏览器)以及何时执行此代码以使此语句成立,当然也取决于它成立的原因。
In Chrome,他们将最小 1ms 设置为传递给 setTimeout
:
的超时值
base::TimeDelta interval_milliseconds =
std::max(base::TimeDelta::FromMilliseconds(1), interval);
消息 任务没有超时,因此会立即排队。因此,如果没有其他任务要处理,它将是下一个执行的任务,远在 1 毫秒超时解决之前。
In Firefox,它们将 setTimeout
安排的任务视为低优先级,从页面加载 安排的任务(这意味着在 Firefox 中,消息任务实际上会在 setTimeout
之后触发,如果两者都安排在页面加载之后:
function test() {
setTimeout(function setTimeout1() {
console.log('setTimeout1')
}, 0)
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1() {
console.log('postMessage');
Promise.resolve().then(function promise1() {
console.log('promise1');
})
};
channel.port2.postMessage(0);
setTimeout(function setTimeout2() {
console.log('setTimeout2')
}, 0);
console.log('sync');
}
console.log( 'testing @ page load' );
test();
setTimeout(() => {
console.log( 'testing after page load' );
test();
}, 1000 );
/* results in Firefox:
testing @ page load
sync
postMessage
promise1
setTimeout1
setTimeout2
testing after page load
sync
setTimeout1
setTimeout2
postMessage
promise1
*/
).
因此,在页面加载的这种特殊情况下,他们会将消息任务视为比超时任务更重要,并且任务执行者必须选择下一个要执行的任务(作为 first step of the Event Loop processing model 的一部分) ), 它将在超时后选择消息。
但这些都是实现上的怪癖,规范中没有任何内容将这种行为正式化。
var con = document.getElementById('con');
con.onclick = function () {
Promise.resolve().then(function Promise1() {
con.textContent = 0;
// requestAnimationFrame(() => con.textContent = 0)
});
};
<div id="con">this is con</div>
为什么这段代码执行微任务后不触发渲染?
setTimeout(function setTimeout1() {
console.log('setTimeout1')
}, 0)
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1() {
console.log('postMessage');
Promise.resolve().then(function promise1() {
console.log('promise1');
})
};
channel.port2.postMessage(0);
setTimeout(function setTimeout2() {
console.log('setTimeout2')
}, 0);
console.log('sync');
为什么postmessage先于timer执行?
Why this code does not trigger rendering after performing microtasks?
确实如此,否则您将看不到正在更新的文本...
也许您无法从您的开发工具中分辨出来?
这 可能 因为鼠标事件现在通常被限制为屏幕刷新率,这意味着当调度鼠标事件的任务将 运行,你' d 已经在画框中,这可能是出于其他原因(因为据我所知,mousemove 事件以这种方式被限制,而不是 click ...).
因此,您的 Promise callback will get executed synchronously (with only the sixth step "set currentTask to null" in between), before the update the rendering 步骤开始,所有开发工具将看到一个正常的绘画框架,就像它预期的那样。
所以可能,开发工具不会在这里显示任何特别的东西,但考虑到你的主张的广泛性,很难确定一个具体原因,这只是我的理论。
您可以尝试通过从此类事件内部调用 requestAnimationFrame
来验证该理论,并检查它是否确实在同一事件循环迭代中执行:
onclick = (evt) => {
console.clear();
setTimeout( () => console.log( 'timeout' ), 0 );
requestAnimationFrame( () => console.log( 'rAF' ) );
};
Click anywhere<br>
If "rAF" gets logged before "timeout", the click event got handled in a painting frame.
对我来说,它在 Chrome 中经常出现,而在 Firefox 中只是偶尔出现一次,但同时我知道 Chrome's rAF is broken... 所以这个理论非常薄弱。
Why postmessage is executed before timer?
这将取决于用户代理(浏览器)以及何时执行此代码以使此语句成立,当然也取决于它成立的原因。
In Chrome,他们将最小 1ms 设置为传递给 setTimeout
:
base::TimeDelta interval_milliseconds =
std::max(base::TimeDelta::FromMilliseconds(1), interval);
消息 任务没有超时,因此会立即排队。因此,如果没有其他任务要处理,它将是下一个执行的任务,远在 1 毫秒超时解决之前。
In Firefox,它们将 setTimeout
安排的任务视为低优先级,从页面加载 安排的任务(这意味着在 Firefox 中,消息任务实际上会在 setTimeout
之后触发,如果两者都安排在页面加载之后:
function test() {
setTimeout(function setTimeout1() {
console.log('setTimeout1')
}, 0)
var channel = new MessageChannel();
channel.port1.onmessage = function onmessage1() {
console.log('postMessage');
Promise.resolve().then(function promise1() {
console.log('promise1');
})
};
channel.port2.postMessage(0);
setTimeout(function setTimeout2() {
console.log('setTimeout2')
}, 0);
console.log('sync');
}
console.log( 'testing @ page load' );
test();
setTimeout(() => {
console.log( 'testing after page load' );
test();
}, 1000 );
/* results in Firefox:
testing @ page load
sync
postMessage
promise1
setTimeout1
setTimeout2
testing after page load
sync
setTimeout1
setTimeout2
postMessage
promise1
*/
因此,在页面加载的这种特殊情况下,他们会将消息任务视为比超时任务更重要,并且任务执行者必须选择下一个要执行的任务(作为 first step of the Event Loop processing model 的一部分) ), 它将在超时后选择消息。
但这些都是实现上的怪癖,规范中没有任何内容将这种行为正式化。