单线程的JavaScript
众所周知,我们的JavaScript是一种单线程语言,之所以是单线程,是由它的工作目的及用途来决定的:作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。如果JavaScript不是单线程语言就很容易造成一些冲突。比如,假定JavaScript同时有两个线程,一个线程是在某个DOM节点上添加内容,而另一个线程是删除了这个节点,这时浏览器应该以哪个线程为准?
那么问题来了,既然JavaScript是单线程语言的话,那它是如何实现一些异步操作的呢?
答案就是我们今天要讲的主题:JavaScript的事件循环
在讲事件循环之前,我们要先讲一些基础的知识点,方便同学们将整个事件循环更清楚,更深刻的串联起来。
浏览器
虽然JS引擎是单线程的,但是它的宿主环境(浏览器或node)却是多线程的,即浏览器内核中还有其他一些线程可以用来辅助JS引擎线程的执行。
下面列出一些浏览器的线程,同学们可以有个大概了解:
- GUI渲染引擎线程
- JS引擎线程
- 定时器触发线程
- 浏览器事件线程
- http异步线程
- ……
JS引擎线程
JS引擎线程,我们也可以称之为主线程,它的主要作用是运行JS同步代码,举例:
var a = 1 //1
console.log() //2
setTimeout() //3
ajax() //4
复制代码
其中1,2行代码是同步代码,直接在主线程中执行,第3,4行代码是异步函数,被主线程分别分配给定时器触发线程和http异步线程这两个处理异步代码的线程进行执行
异步操作进程
我们前面提到的定时器触发线程,浏览器事件线程以及http异步线程等都是异步操作进程,这些进程主要是处理主线程分配的异步任务,这些被主线程扔过来的异步任务都会指定各自的回调函数,当异步操作进程执行完任务之后,会将返回的回调函数发送给任务队列。
任务队列
我们可以将任务队列看成是一个静态的队列存储结构(先进先出),这个存储结构中存放的是一些由异步操作进程扔过来的回调函数(是在异步任务完成之后,才将其回调函数扔在任务队列中。比如浏览器事件进程要处理一个点击事件,该事件有一个回调函数,是在点击事件完成之后,浏览器事件进程才将绑定的回调函数发送给任务队列)
Event loop
Event loop可以理解成一项任务或工作,主要作用是检测任务队列和主线程生成的执行栈,一旦执行栈中的代码执行完之后,就会自动从任务队列中抽取回调函数。
Event loop也可以理解成一种机制或运行模式,可以将:主线程分配异步任务给异步操作线程,异步操作线程执行任务完成后分配回调函数给任务队列,任务队列保存回调函数,然后当主线程执行完代码后会抽取任务队列中的回调函数。这一项循环称之为Event loop
每个人都有自己的理解方式和思维想法,不管怎么样理解记忆,我的这篇文章让同学们明白了Event loop怎么运行就好。
尾
在我们的实际开发中也许并不需要理解Evnet loop对异步操作的影响,但在我们对其进行了一定程度上的学习和理解之后,我们会对一个包含异步操作的脚本/函数的执行有更加明确的认知,更加清楚它在执行中处于一个怎样的位置和状态