一道面试题引发的思考 --- Event Loop

想必面试题刷的多的同学对下面这道题目不陌生,能够立即回答出输出10个10,可是你真的懂为什么吗?为什么是输出10?为什么是输出10个10?这两个问题在我脑边萦绕。嗯,我得说服自己。

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}

JavaScript 是单线程。(ok,我又问自己它为什么是单线程 ==》作为浏览器语言,JS的用途是与用户交互以及操作DOM,如果是多线程会引发很多问题,浏览器无法判断以哪个线程为标准,因此它只能是单线程)
任务分为「同步任务」与「异步任务」。同步任务都好理解,一个执行完执行下一个。异步任务稍许复杂。
异步任务运行机制:

  • 同步任务在「主线程」上执行,形成一个「执行栈」(execution context stack)
  • 主线程之外,还有一个「任务队列」(task queue),异步任务有了运行结果,就在任务队列里放置一个事件
  • 执行栈中同步任务执行完,就会去读取任务队列的事件,异步任务事件结束等待状态,进入执行栈执行
  • 主线程重复前面三步

其实屡清楚很好理解,以上运行机制又称为 Event Loop(事件循环)
我们回到这道面试题, 一起来理解下:

for 循环是同步任务,setTimeout 是异步任务
首先 for 循环在主线程上执行,setTimeout 进入任务队列
同步任务执行完,i = 10,此时 setTimeout 被唤醒进入执行栈执行
因此输出的值为10
可是为什么会输出10个10呢?到现在我还是没有完全说服自己。

我们对代码稍作修改,

for (var i = 0; i < 10; i++) {
  console.log(i)
  var p = setTimeout(() => {
    console.log(i)
  }, 0)
  console.log(p)
}

输出结果

0
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: TimersList, _idleStart: 1893, …}
1
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 3739, …}
2
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 4924, …}
......
9
Timeout {_called: false, _idleTimeout: 1, _idlePrev: TimersList, _idleNext: Timeout, _idleStart: 11733, …}
10
10
...
10

看到这个结果大家是否有些清楚了呢,我们重新梳理下原先的面试题

首先 for 循环在主线程上执行,setTimeout 内部的回调函数进入任务队列
for 循环里,i 每次执行一次,异步队列里放置一个 setTimeout 回调
同步任务执行完,i = 10, 此时异步队列里放置了10个回调事件
setTimeout 被唤醒进入执行栈执行
因此输出了10个10

当然了, Event Loop 的知识不止这点,涉及到的东西也很多。本文只是我对这道面试题的一点思考,有误的地方望批评指正。

以下几篇是我收藏的好文,供大家学习参考~
JavaScript 运行机制详解:再谈Event Loop
这一次,彻底弄懂 JavaScript 执行机制
从event loop规范探究javaScript异步及浏览器更新渲染时机

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值