# JavaScript 运行机制-EventLoop
# 单线程
JavaScript 是单线程语言,为什么不支持多线程呢?这是 JavaScript 的运行环境所导致的。假如有两个线程,他们同时操作同一个DOM节点,那么这个节点应该采用哪个线程的结果?
虽然HTML5提出了 web Worker 标准, 但此标准创建的线程并不能修改 DOM 所以依然没有改变 JavaScript 单线程的本质。
# 任务队列
既然是单线程,那么所有的任务都要排队执行。一旦某些任务耗时很长,后面的任务就需要一直等待。
耗时的任务,我们一般都要把它们改为异步任务。例如:
// 这部分代码会放在下次循环时的第一个执行
setTimeout(() => {
// 耗时任务
}, 0)
此时 JavaScript 的设计者就考虑,能否把异步任务任务存放在一个临时的任务队列中。等主进程执行完毕后,再把这些和异步任务相关的任务加载到主线程的末尾继续执行。
于是乎这个临时的队列,我们可以把它称之为 消息队列
。
当主进程的代码都执行完毕后,去 消息队列
中查看之前丢进去的耗时任务执行完了没有。如果有执行完毕的那就拿出来放在主进程的末尾继续执行。
由此我们可以看出,JavaScript 运行时至少是有两个进程
主进程
消息队列
又叫消息进程
所以 JavaScript 代码执行的过程是这样的
1. 所有的同步代码,都在 `主进程` 上执行
2. `主进程` 执行的过程中遇到异步任务时
3. 当主进程执行完毕时,立刻去 `消息进程` 中把未执行的一步任务放在主进程的末尾继续执行。
4. 主线程会不断的重复上面的第三步,所以才把这个过程叫做 EventLoop
# setTimeout 带来的问题
我们先来看这一段代码
let start = Date.now()
setTimeout(() => {
console.log('异步任务')
}, 200)
console.log(start)
for (let i = 0; i < 1e4; i++) {
console.log(i)
}
console.log(Date.now() - start)
我们希望在程序开始执行 200 毫秒后,在控制台打印 异步任务
。但执行的结果并非如你所愿,因为主进程中有一个非常耗时的同步任务。
还记得我们前面说过 EventLoop 是要等待主进程执行完毕,才会去消息进程中拿之前订阅过的异步任务。 虽然我们订阅的是 200 毫秒,但很可惜主进程的执行锁耗费的时间 >200 毫秒。所以 setTimeout 并不能准时的帮我们完成这样的操作。
setInterval 也有同样的问题。