前言
居然考到了事件循环,好偏底层的东西。我以为是事件冒泡或事件委托呢。既然被问到了,那么就写一下吧。
是什么
Java和Python都是多线程模式(通常为Thread类)语言。但JavaScript是单线程、非阻塞的。这与它的用途有关:
Java通常用来开发系统级应用,Python原本的用途就是系统脚本。JavaScript最早是用来做网页交互逻辑以及操作DOM的
所以JavaScript被设计成了实质上的单线程语言。那么如何多任务并发呢?多线程语言可以用Thread类,JS用到了事件循环模型。
基础知识
调用栈 (stack)
观察下列代码:
// 来源:[3]
function foo(b) {
let a = 10;
return a + b + 11;
}
function bar(x) {
let y = 3;
return foo(x * y);
}
console.log(bar(7)); // 返回 42
MDN的讲解:
函数调用形成了一个由若干帧组成的栈。
当调用 bar 时,第一个帧被创建并压入栈中,帧中包含了 bar 的参数和局部变量。 当 bar 调用 foo 时,第二个帧被创建并被压入栈中,放在第一个帧之上,帧中包含 foo 的参数和局部变量。当 foo 执行完毕然后返回时,第二个帧就被弹出栈(剩下 bar 函数的调用帧 )。当 bar 也执行完毕然后返回时,第一个帧也被弹出,栈就被清空了。
堆 (heap)
对象被分配在堆(heap)中,用于表示一大块的内存区域。
消息队列(queue)
一个JS runtime包含一个待处理信息的队列。每一个消息都关联着一个用以处理这个消息的回调函数。
在事件循环期间的某个时刻,运行时会从最先进入队列中的消息。被处理的消息被移出队列 ,并成为回调函数的关联参数。
函数的处理会一直进行到栈再次为空为止。
异步原理
任务队列 (task queue)
单线程语言意味着任务都需要排队,前一个任务结束才会执行后一个任务。
任务队列主要是给异步任务用的。不进入主线程、而进入任务队列的任务就是异步执行。
执行机制
- 所有同步任务都在主线程上执行,形成执行栈。
- 主线程之外还存在一个任务队列。只要多任务运行有了结果就会在任务队列中放置一个事件。
- 一旦执行栈中所有同步任务执行完毕,系统就会读取任务队列中的事件。对应的异步任务结束等待状态,并开始执行。
- 主线程不断重复上面的一步。
事件循环 (Event Loop)
主线程从任务队列中读取事件,这个过程是循环不断的。
图源自阮一峰2。
主线程运行时候,产生堆和栈,栈中的代码调用各种外部外部API,在任务队列中加入各种事件。只要栈中的代码执行完毕就会读取任务队列,依次执行事件中对应的回调函数。