setTimeout为什么最小只能设置4ms,如何实现一个0ms的setTimeout?
更多描述 可参考两篇文章
Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 708
Author 回答者: shfshanyue
postMessage
Author 回答者: hengistchan
blink引擎的DOMTimer类源码
前几天刚好找了一下源码,在47-49行设置了kMaxTimerNestingLevel
和 kMinimumInterval
两个变量分别为5和4,
分别表示最大的嵌套层数和最小的毫秒数
截取一下DOMTimer类的部分代码
DOMTimer::DOMTimer(ExecutionContext* context,
ScheduledAction* action,
base::TimeDelta timeout,
bool single_shot,
int timeout_id)
: ExecutionContextLifecycleObserver(context),
TimerBase(nullptr),
timeout_id_(timeout_id),
// Step 9:
nesting_level_(context->Timers()->TimerNestingLevel()),
action_(action) {
DCHECK_GT(timeout_id, 0);
// Step 10:
if (timeout < base::TimeDelta())
timeout = base::TimeDelta();
// Steps 12 and 13:
// Note: The implementation increments the nesting level before using it to
// adjust timeout, contrary to what the spec requires crbug.com/1108877.
IncrementNestingLevel();
// Step 11:
// Note: The implementation uses >= instead of >, contrary to what the spec
// requires crbug.com/1108877.
if (nesting_level_ >= kMaxTimerNestingLevel && timeout < kMinimumInterval)
timeout = kMinimumInterval;
// Select TaskType based on nesting level.
TaskType task_type;
if (timeout.is_zero()) {
task_type = TaskType::kJavascriptTimerImmediate;
DCHECK_LT(nesting_level_, kMaxTimerNestingLevel);
} else if (nesting_level_ >= kMaxTimerNestingLevel) {
task_type = TaskType::kJavascriptTimerDelayedHighNesting;
} else {
task_type = TaskType::kJavascriptTimerDelayedLowNesting;
}
MoveToNewTaskRunner(context->GetTaskRunner(task_type));
// Clamping up to 1ms for historical reasons crbug.com/402694.
timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(1));
看代码中setp11的那部分,当嵌套层数大于5且timeout小于4ms时,timeout才会等于4ms 然后(代码最后一行),timeout 还会和1ms作比较取最大值,作为最终的timeout
Author 回答者: DanielLeefu
let timeouts = [];
const messageName = 'zero-settimeout'
function setTimeoutZero(fn) {
timeouts.push(fn);
window.postMessage(messageName, '*')
}
function handleMessage (evt) {
if (evt.source == window && evt.data === messageName ) {
if (timeouts.length > 0) {
const f = timeouts.shift()
f()
}
}
}
window.addEventListener('message', handleMessage)
window.zeroSettimeout = setTimeoutZero;