JavaScript 运行 是否超出超时 ID?
Does JavaScript run out of timeout IDs?
令人惊讶的是,我在网络上的任何地方都找不到这个问题的答案。
文档中it is stated that setTimeout and setInterval share the same pool of ids, as well as that an id will never repeat. If that is the case then they must eventually run out because there is a maximum number电脑可以处理吗?那么会发生什么,你不能再使用超时了?
TL;DR;
这取决于浏览器的引擎。
在 Blink 和 Webkit 中:
- 定时器最大并发数为231-1.
- 如果您尝试使用更多,您的浏览器可能会因无限循环而死机。
官方规格
来自W3C docs:
The setTimeout()
method must run the following steps:
Let handle be a user-agent-defined integer that is greater than zero that will identify the timeout to be set by this call.
Add an entry to the list of active timeouts for handle.
[...]
还有:
Each object that implements the WindowTimers
interface has a list of active timeouts and a list of active intervals. Each entry in these lists is identified by a number, which must be unique within its list for the lifetime of the object that implements the WindowTimers
interface.
注意:虽然 W3C 提到了两个列表,但 WHATWG spec 确定 setTimeout
和 setInterval
共享一个共同的 活动计时器列表 。这意味着您可以使用 clearInterval()
删除由 setTimeout()
创建的计时器,反之亦然。
基本上,每个用户代理都可以随意实现 handle Id,唯一的要求是 integer 唯一对于每个对象;您可以获得与浏览器实现一样多的答案。
让我们看看 Blink 在做什么。
闪烁实现
之前的说明: to find the actual source code of Blink. It belongs to the Chromium codebase which is mirrored in GitHub. I will link GitHub (its current latest tag: 72.0.3598.1
) because its better tools to navigate the code. Three years ago, they were pushing commits to chromium/blink/. Nowadays, active development is on chromium/third_party/WebKit but there is a discussion 正在进行新的迁移。
在 Blink 中(以及在 WebKit 中,显然具有非常相似的代码库),负责维护上述活动计时器列表的是 DOMTimerCoordinator
belonging to each ExecutionContext
.
// Maintains a set of DOMTimers for a given page or
// worker. DOMTimerCoordinator assigns IDs to timers; these IDs are
// the ones returned to web authors from setTimeout or setInterval. It
// also tracks recursive creation or iterative scheduling of timers,
// which is used as a signal for throttling repetitive timers.
class DOMTimerCoordinator {
DOMTimerCoordinator
将计时器存储在 blink::HeapHashMap
(alias TimeoutMap
) collection timers_
中,哪个键是(符合规范)int
类型:
using TimeoutMap = HeapHashMap<int, Member<DOMTimer>>;
TimeoutMap timers_;
这回答了你的第一个问题(在 Blink 的上下文中):每个上下文的最大活动计时器数是 231-1;远低于您提到的 JavaScript MAX_SAFE_INTEGER
(253-1),但对于正常用例来说仍然绰绰有余。
对于你的第二个问题,“然后会发生什么,你不能再使用超时了?”,到目前为止我只有部分答案。
新计时器由 DOMTimerCoordinator::InstallNewTimeout()
. It calls the private member function NextID()
to retrieve an available integer key and DOMTimer::Create
创建,用于实际创建计时器对象。然后,它将新计时器和相应的密钥插入 timers_
.
int timeout_id = NextID();
timers_.insert(timeout_id, DOMTimer::Create(context, action, timeout,
single_shot, timeout_id));
NextID()
从1到2循环获取下一个id31-1:
int DOMTimerCoordinator::NextID() {
while (true) {
++circular_sequential_id_;
if (circular_sequential_id_ <= 0)
circular_sequential_id_ = 1;
if (!timers_.Contains(circular_sequential_id_))
return circular_sequential_id_;
}
}
它将circular_sequential_id_
的值递增1,如果超出上限则将其设置为1(尽管INT_MAX
+1调用UB,大多数C实现returnINT_MIN
).
因此,当 DOMTimerCoordinator
用完 ID 时,从 1 开始重试,直到找到一个可用的。
但是,如果它们都在使用中会怎样?是什么阻止了 NextID()
进入死循环?它。很可能,Blink 开发人员在假设永远不会同时存在 231-1 个计时器的情况下编码 NextID()
。这说得通;对于 DOMTimer::Create()
编辑的每个字节 return 如果它已满,您将需要 GB 的 RAM 来存储 timers_
。如果您存储长回调,它可以加起来达到 TB。更不用说创建它们所需的时间了。
总之,没有实现无限循环的保护看起来很奇怪,所以我有contacted Blink developers,但到目前为止我没有任何反应。如果他们回复,我会更新我的答案。
令人惊讶的是,我在网络上的任何地方都找不到这个问题的答案。
文档中it is stated that setTimeout and setInterval share the same pool of ids, as well as that an id will never repeat. If that is the case then they must eventually run out because there is a maximum number电脑可以处理吗?那么会发生什么,你不能再使用超时了?
TL;DR;
这取决于浏览器的引擎。
在 Blink 和 Webkit 中:
- 定时器最大并发数为231-1.
- 如果您尝试使用更多,您的浏览器可能会因无限循环而死机。
官方规格
来自W3C docs:
The
setTimeout()
method must run the following steps:
Let handle be a user-agent-defined integer that is greater than zero that will identify the timeout to be set by this call.
Add an entry to the list of active timeouts for handle.
[...]
还有:
Each object that implements the
WindowTimers
interface has a list of active timeouts and a list of active intervals. Each entry in these lists is identified by a number, which must be unique within its list for the lifetime of the object that implements theWindowTimers
interface.
注意:虽然 W3C 提到了两个列表,但 WHATWG spec 确定 setTimeout
和 setInterval
共享一个共同的 活动计时器列表 。这意味着您可以使用 clearInterval()
删除由 setTimeout()
创建的计时器,反之亦然。
基本上,每个用户代理都可以随意实现 handle Id,唯一的要求是 integer 唯一对于每个对象;您可以获得与浏览器实现一样多的答案。
让我们看看 Blink 在做什么。
闪烁实现
之前的说明:72.0.3598.1
) because its better tools to navigate the code. Three years ago, they were pushing commits to chromium/blink/. Nowadays, active development is on chromium/third_party/WebKit but there is a discussion 正在进行新的迁移。
在 Blink 中(以及在 WebKit 中,显然具有非常相似的代码库),负责维护上述活动计时器列表的是 DOMTimerCoordinator
belonging to each ExecutionContext
.
// Maintains a set of DOMTimers for a given page or
// worker. DOMTimerCoordinator assigns IDs to timers; these IDs are
// the ones returned to web authors from setTimeout or setInterval. It
// also tracks recursive creation or iterative scheduling of timers,
// which is used as a signal for throttling repetitive timers.
class DOMTimerCoordinator {
DOMTimerCoordinator
将计时器存储在 blink::HeapHashMap
(alias TimeoutMap
) collection timers_
中,哪个键是(符合规范)int
类型:
using TimeoutMap = HeapHashMap<int, Member<DOMTimer>>;
TimeoutMap timers_;
这回答了你的第一个问题(在 Blink 的上下文中):每个上下文的最大活动计时器数是 231-1;远低于您提到的 JavaScript MAX_SAFE_INTEGER
(253-1),但对于正常用例来说仍然绰绰有余。
对于你的第二个问题,“然后会发生什么,你不能再使用超时了?”,到目前为止我只有部分答案。
新计时器由 DOMTimerCoordinator::InstallNewTimeout()
. It calls the private member function NextID()
to retrieve an available integer key and DOMTimer::Create
创建,用于实际创建计时器对象。然后,它将新计时器和相应的密钥插入 timers_
.
int timeout_id = NextID();
timers_.insert(timeout_id, DOMTimer::Create(context, action, timeout,
single_shot, timeout_id));
NextID()
从1到2循环获取下一个id31-1:
int DOMTimerCoordinator::NextID() {
while (true) {
++circular_sequential_id_;
if (circular_sequential_id_ <= 0)
circular_sequential_id_ = 1;
if (!timers_.Contains(circular_sequential_id_))
return circular_sequential_id_;
}
}
它将circular_sequential_id_
的值递增1,如果超出上限则将其设置为1(尽管INT_MAX
+1调用UB,大多数C实现returnINT_MIN
).
因此,当 DOMTimerCoordinator
用完 ID 时,从 1 开始重试,直到找到一个可用的。
但是,如果它们都在使用中会怎样?是什么阻止了 NextID()
进入死循环?它NextID()
。这说得通;对于 DOMTimer::Create()
编辑的每个字节 return 如果它已满,您将需要 GB 的 RAM 来存储 timers_
。如果您存储长回调,它可以加起来达到 TB。更不用说创建它们所需的时间了。
总之,没有实现无限循环的保护看起来很奇怪,所以我有contacted Blink developers,但到目前为止我没有任何反应。如果他们回复,我会更新我的答案。