JS Event-Loop

最近在看JavaScriptGeneratorPromise等实现异步的方法。对于一直用Objective-C编程的我来说,是有些云里雾里。故查了些资料,了解相关的内容,遂有此文。

为什么JavaScript是单线程的语言

首先,我们要解释下,JS是单线程的,并不是说JS中只有一个线程,而是在JS引擎中负责解释和执行JS语句的线程只有一个,我们称之为主线程。

JS中,还是有很多其他线程存在的,比如:处理DOM事件的线程,定时器线程,IO线程等。

为什么JS中只有一个线程呢? 这里要说下JS诞生的背景,它是浏览器中执行脚本的语言,主要用途是和用户交互,为了提升体验,避免多线程带来的同步等问题,简化整个模型,采用了单线程。(Objective-C也是为了交互而生,但是并没有采用单线程,而是支持多线程,我个人觉得这个更多是设计思想的选择,而JS这样设计确实带来了更简单的模式)

在HTML5中,提出了Web Worker标准,允许JS创建多个线程,但是规定子线程受主线程控制,且不能操作DOM。

JS任务管理

我们先看一张图,了解下JS中是如何管理和执行任务的:

这里分为三个区域:

  • 执行栈(主线程)
  • 任务队列(其他线程)
  • 堆 (对象)

主线程任务执行栈

主线程的任务使用栈来实现任务执行和调用,这里用栈是因为栈的结构方便实现函数调用顺序的管理。
这里简单举个例子:

1
2
3
4
5
6
7
8
9
10
let funcA = (x) => {
return x * 2;
}
let funcB = (x) => {
return x + 2;
}
console.log(funcB(funcA(2)));

栈中执行顺序就是

  • funcA
  • funcB
  • console.log

任务队列

每当遇到需要异步执行的任务(请求网络、IO等),JS不会阻塞等待结果,而是往下面执行,当异步的任务执行了之后,系统发送通知出来,然后等待主线程任务栈执行完毕后,执行这些异步回调。

流程如图,就是所谓的Event Loop

注意,这里即使异步的任务很快执行完毕,也会等到执行栈中的任务执行完毕后,才执行,这里用setTimeout来举个例子:

1
2
3
console.log(1);
setTimeout(()=>{console.log(2)},0)
console.log(3);

这里虽然延迟是0ms,但是输出依然是:

1
2
3
1
3
2

所以setTimeout等的延迟时间,其实是不准确的,如果主线程一直很忙,是不会执行到的。

这里堆就是用来管理系统中的对象,大部分的语言应该都是使用堆来管理对象(我知道的),这里就不多解释。

总结

这篇文章主要是介绍了JS为什么采用了单线程的思想,然后介绍了JS中是如何任务管理是如何通过主线程+任务队列的方式来实现,从而实现了Event Loop

参考内容: