javascript 中的执行顺序
Execution order in javascript
在下面的代码中,2的延迟是0秒,那为什么2输出在3之后而不是之前呢?
(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 0);
console.log(3);
})();
setTimeout()
不会延迟在它之后调用的所有内容;它只会延迟内部功能。因此它为 console.log(2)
启动一个计时器,继续执行剩余的代码 console.log(3)
,然后在计时器用完时运行超时函数。
将执行以下事项:
1
3个
2
setTimeout
的实现方式是在 最小 给定延迟后执行,一旦浏览器的线程可以自由执行它。因此,举例来说,如果您为 delay 参数指定一个值 0,并且您认为它会“立即”执行,那么它不会。更准确地说,它将在下一个事件周期中 运行(这是事件循环的一部分 - 负责执行代码的并发模型)。
因此,打印 1 和 3 实际上会被推送到调用堆栈并立即执行,因为 setTimeout 回调函数只能从事件队列中拉出,以便在下一个事件中选择循环打勾。
请阅读延迟时间超过指定时间的原因 - https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
关于 JS 事件循环的更多信息 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
setTimeout(() => console.log(2), 0)
和运行宁console.log(2)
不是一回事。该系统有点复杂,但主要思想是你有一个事件循环来管理来自两个主要的大队列的回调:分别称为回调队列和微任务队列。这个事件循环不断地跟踪 call stack
是否为空,以及回调队列中是否有回调要处理。只有当调用堆栈执行完所有顶层代码时,事件循环才会获取回调队列中的任何内容,并运行将其作为调用堆栈中的一个简单函数。
让我们逐行查看您的示例,看看如何填充调用堆栈。
压入堆栈的第一件事是具有顶级代码的全局执行上下文。现在你的函数是 IIFY,这意味着加载脚本时将立即 运行。
(</empty/>)
(global execution context)
callback queue
->
- 加载 function() 后,我们有
(function() execution context)
(global execution context)
当函数执行上下文运行s逐行输出1,然后在定时器超时时将setTimeout(就是一个console.log(2)
)的回调推入回调队列
callback queue
-> <console.log(2)
> 注意:函数没有 运行 在主调用堆栈中,它在回调队列中被事件循环排队。
之后它将 运行 console.log(3)
并且调用堆栈将 return 来自函数执行上下文,然后来自全局执行上下文。因此,我们最终得到一个空的调用堆栈
(</empty/>)
(</empty/>)
- 事件循环注意到调用堆栈是空的,所以现在它委托存储在调用堆栈内的回调队列中的函数回调并 运行s 它们。最终输出
1 3 2
这个自调用的匿名函数声明
function (function() {
console.log(1);
setTimeout(function(){console.log(2)}, 0);
console.log(3);
})();
和你写的一样
console.log(1);
setTimeout( console.log, 0, 2);
console.log(3);
并且日志的顺序将是相同的 - 无论是在匿名定时函数中包装还是解包 - 因为它将在 运行时 期间解析,而 setTimeout
声明是一个程序,并且要等到程序开始执行。
it/this 将在 JITC 完成编译您的代码之后。
在下面的代码中,2的延迟是0秒,那为什么2输出在3之后而不是之前呢?
(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 0);
console.log(3);
})();
setTimeout()
不会延迟在它之后调用的所有内容;它只会延迟内部功能。因此它为 console.log(2)
启动一个计时器,继续执行剩余的代码 console.log(3)
,然后在计时器用完时运行超时函数。
将执行以下事项:
1 3个 2
setTimeout
的实现方式是在 最小 给定延迟后执行,一旦浏览器的线程可以自由执行它。因此,举例来说,如果您为 delay 参数指定一个值 0,并且您认为它会“立即”执行,那么它不会。更准确地说,它将在下一个事件周期中 运行(这是事件循环的一部分 - 负责执行代码的并发模型)。
因此,打印 1 和 3 实际上会被推送到调用堆栈并立即执行,因为 setTimeout 回调函数只能从事件队列中拉出,以便在下一个事件中选择循环打勾。
请阅读延迟时间超过指定时间的原因 - https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
关于 JS 事件循环的更多信息 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
setTimeout(() => console.log(2), 0)
和运行宁console.log(2)
不是一回事。该系统有点复杂,但主要思想是你有一个事件循环来管理来自两个主要的大队列的回调:分别称为回调队列和微任务队列。这个事件循环不断地跟踪 call stack
是否为空,以及回调队列中是否有回调要处理。只有当调用堆栈执行完所有顶层代码时,事件循环才会获取回调队列中的任何内容,并运行将其作为调用堆栈中的一个简单函数。
让我们逐行查看您的示例,看看如何填充调用堆栈。 压入堆栈的第一件事是具有顶级代码的全局执行上下文。现在你的函数是 IIFY,这意味着加载脚本时将立即 运行。
(</empty/>)
(global execution context)
callback queue
->
- 加载 function() 后,我们有
(function() execution context)
(global execution context)
当函数执行上下文运行s逐行输出1,然后在定时器超时时将setTimeout(就是一个console.log(2)
)的回调推入回调队列
callback queue
-> <console.log(2)
> 注意:函数没有 运行 在主调用堆栈中,它在回调队列中被事件循环排队。
之后它将 运行 console.log(3)
并且调用堆栈将 return 来自函数执行上下文,然后来自全局执行上下文。因此,我们最终得到一个空的调用堆栈
(</empty/>)
(</empty/>)
- 事件循环注意到调用堆栈是空的,所以现在它委托存储在调用堆栈内的回调队列中的函数回调并 运行s 它们。最终输出
1 3 2
这个自调用的匿名函数声明
function (function() {
console.log(1);
setTimeout(function(){console.log(2)}, 0);
console.log(3);
})();
和你写的一样
console.log(1);
setTimeout( console.log, 0, 2);
console.log(3);
并且日志的顺序将是相同的 - 无论是在匿名定时函数中包装还是解包 - 因为它将在 运行时 期间解析,而 setTimeout
声明是一个程序,并且要等到程序开始执行。
it/this 将在 JITC 完成编译您的代码之后。