Event-loop 事件循环
首先来看一段代码
function fn(){
console.log(‘1’)
setTimeout(() => {
console.log(‘2’)
}, 1000)
var aa = 0
for (let i = 0; i < 9999999999; i++) {
aa = i
}
if (aa = 9999999998) {
console.log(‘3’)
}
}
fn()
运行结果是先输出 1,然后大概好几秒(大于一秒)以后依次输出 3,2。
setTimeout(() => {
console.log(‘2’)
}, 1000)
可是上边这里明明写了定时器一秒后输出字符串 2 啊,为什么过了好久才输出呢?这里就会引发思考,js 到底是怎么执行的
什么是 JS 事件循环
先来一张经典图
我们都知道 JS 是单线程的,所以在它的 stack(执行栈)里面任务是排队执行的,这里我们在回头看开始的代码
function fn(){
console.log(‘1’)
setTimeout(() => {
console.log(‘2’)
}, 1000)
var aa = 0
for (let i = 0; i < 9999999999; i++) {
aa = i
}
if (aa = 9999999998) {
console.log(‘3’)
}
}
fn()
当调用 fn() 的时候,就会把 fn 这个函数放到 stack 中去。执行步骤分以下几步
第一步:执行 console.log(‘1’) 第二步:执行到 setTimeout 的时候,因为我们都知道 setTimeout 是异步操作,这里不可能说我 js 停下来等你 1 秒,这样页面就卡死在那里了。
这里在回过头来看上面那 Event-loop 图
注:js 是单线程,但 js 是运行在浏览器中的,浏览器是多线程的,这一点要搞清楚
当我们运行到 setTimeout 这里时,为了不阻塞页面,浏览器会在开一个线程来处理异步的操作,也就是上图红框框部分。然后 js 会略过 setTimeout,继续执行下面 for 循环的代码。当一秒钟之后 setTimeout 执行完毕,就会将结果放如到 callback queue(回调队列中等待调用),等待 stack 中的任务执行完毕后来调用它,所以一开始 fn() 函数的执行结果是 1,3,2,、不是 1,2,3. 就是因为异步的操作被放在了 callback queue 中,等待 stack 中的执行完才会去找它。
现在我们搞明白了为什么结果是 1,3,2 而不是 1,2,3, 之后还有一个问题就是为什么 setTimeout 明明写的是 1 秒之后在控制台打印出 ’2’ 来,为什么实际体验中要好几秒之后呢。原因就在以下这部分代码中
for (let i = 0; i < 9999999999; i++) {
aa = i
}
if (aa = 9999999998) {
console.log(‘3′)
}
原因就是这部分 for 循环的代码执行过程超过了 1 秒。而这个 for 循环是放在 stack 里面的。它执行不完就不会去 callback queue 里面找东西,所以我们看到的最终结果就是
先打印 ’1’ 出来然后等几秒后 (这个随电脑配置不同,时间长短不一样),在打印出 ’2’ 最后才会打印出 ’3’