我有一个简单的程序,如下所示:
console.log("Start of the program execution!");
setTimeout(() => {
console.log("I ran after a second!");
}, 1000);
console.log("The end of the file!");
然后,我的问题是:当计时器运行并且函数没有放入调用堆栈时,匿名函数位于哪里?
如果我有一个普通的函数定义,代码将如下所示:
function doSth()
{
console.log("I ran after a second!");
}
console.log("Start of the program execution!");
setTimeout(doSth, 1000);
console.log("The end of the file!");
然后,在调用堆栈变空后,函数
doSth()
仍会存在于全局内存中,因此 setTimeout()
可以在设定时间后使用其引用来调用它。这种安排对我来说非常有意义。当我使用匿名函数并进行一些观察时,问题就出现了。
为了进行一些观察,我首先将调试器放在程序的第 5 行;
console.log("The end of the file!");
。然后我看一下开发者工具的Scope
。匿名函数无处可见。
为了进行下一步观察,我将调试器放在
console.log()
函数内的 setTimeout()
上,即程序的第 3 行。那么当我们运行它时,调用堆栈上确实有匿名函数。
因此,这两个观察给我带来了困惑。如果在调用
setTimeout()
后匿名函数不存在于全局内存中,那么当 setTimeout()
完成其工作时如何将其推送到调用堆栈?该函数是否“跟随” setTimeout()
到 webAPIs
域,然后从那里返回?
你可以想象
setTimeout()
和 setInterval()
是这样实现的:
class Timer {
static allTimers = {};
static timerId = 0;
constructor(timeout, func, interval) {
this.timerId = ++timerId;
this.timeout = timeout;
this.func = func;
this.interval = interval;
this.expire_time = Date.now() + timeout;
this.allTimers[timerId] = this;
addTimerToEventLoop(this);
}
clear(id) {
let timer = this.allTimers[id];
if (timer) {
removeTimerFromEventLoop(timer);
}
}
function setTimeout(timeout, func) {
let newTimer = new Timer(timeout, func, false);
return newTimer.timerId;
}
function setInterval(timeout, func) {
let newTimer = new Timer(timeout, func, true);
return newTimer.timerId;
}
函数
addTimerToEventLoop()
和 removeTimerFromEventLoop()
是事件循环的内部接口,使其按定时计划运行它们。
这些函数保存在每个
func
对象的 Timer
属性中。如果 Timer
类实际上对用户代码可见,而不是在 JavaScript 引擎内部,那么您将能够在 Timer.allTimers
上找到它们。