深入浅出理解JS事件循环机制

在说JS事件循环机制之前,我们先从操作系统是如何开启一个进程讲起。

一、 操作系统是如何执行 “多任务” 的

什么是多任务?
同时运行多个任务,比如:一边用浏览器上网,一边听歌,一边使用电脑微信聊天,至少有3(3+)个任务同时执行。

  1. mac、linux、windows这些操作系统都是支持“多任务”的操作系统
  2. 这些操作系统是如何实现“多任务”操作的呢?— 由系统的CPU来执行

(1)、单核CPU执行“多任务”:操作系统轮流让各个任务交替执行。例:任务1执行0.01s,然后任务 2执行0.01s,然后任务3执行0.01s,依次这样交替循环下去,但是由于CPU处理速度很快,我们感觉任务在同时进行一样。
(2)、多核CPU执行“多任务”:真正的多任务其实是在多核CPU上实现的,每个核分别去执行一个任务,但是由于任务数量远远大于CPU核的数量,所以多核CPU也会把很多任务轮流调度在每个核上执行。

二、聊聊 “进程” 和 “线程”

什么是进程?
对操作系统来说执行一个任务就是一个进程。
什么是线程?
在一个进程上同时需要执行多个“子任务”。例:在使用word时,同时可以写字,拼写检查,打印等事情,在一个进程内部同时要干多件事,每件事都是一个线程。

  1. 一个进行至少有一个线程。
  2. 线程和进程都是交替执行任务。

三、JS语言的特性:一门单线程非阻塞的语言

  1. 单线程:是由于js最初的用途来决定的,与浏览器交互单线程意味着JS代码在执行的任何时候都是由主线程来处理所有任务
  2. 非阻塞:JS在执行异步任务的时候无法立刻返回结果,需要花时间等待,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定的规则去执行回调函数。

如果JS是多线程,同时对一个DOM进行不同操作的时候会不知道处理的优先级而造成混乱。所以单线程保证了执行的顺序,但是限制了JS执行的效率

四:浏览器下JS引擎的事件循环机制

  1. JS在初次执行代码时:会将不同的变量存于内存中的不同位置。

JS内存存放位置:
1:堆(heap):存放一些对象
2:栈(stack):存放基础数据类型以及堆中对象的指针,注:“执行栈”区别于这个“栈”

  1. JS调用一个方法的过程:

当调用一个方法时,js会生成与这个方法对应的执行环境(context),又叫做上下文,在执行环境中存在这个方法的私有作用域,上层作用域的指向(this对象),方法的参数。

  1. JS中“同步代码”是如何执行的?

(1)、什么是“执行栈”?——用于方法排队等候的“暂存地”

由于JS是单线程,同一时间只能执行一个方法,其余方法需要排队等候依次调用,所以这个排队等候的地方就是“执行栈”

(2)、关于执行栈中的排队顺序

当JS脚本第一次执行的时候,JS引擎会解析代码并将其中的“同步代码”顺序加入到执行栈,然后从头开始执行,如果当前执行的是一个方法,就会在执行栈中添加这个方法的执行环境,当方法执行完毕会退出执行环境并销毁,回到上一个方法的执行环境,这个过程反复执行,直到执行栈中的代码完全执行完毕。
在一个方法的执行环境中,还可以调用别的方法,甚至可调用自己本身,其结果就是在执行环境中再添加一个执行环境,这个过程是可以无限循环下去的,除非发生“栈溢出”(即超过了使用内存的最大值,造成内存泄露)

  1. js中“异步代码”是如何执行的?

(1)、事件队列(task queue)—— 存放异步事件的地方

JS引擎遇到一个异步事件的时候,并不会一直等待结果,而会将这个事件挂起,继续执行“执行栈”中的其他任务,当这个异步事件返回结果后,js会将这个事件加入到另外一个队列中(即事件队列)
异步事件放入到事件队列中不会立即执行,而是等待执行栈中所有的任务都执行完成,主线程处于闲置状态的时候,主线程会去查找事件队列中是否有任务,如果有,按照顺序依次调用
主线程取出第一位事件,并把事件对应的回调函数放到执行栈中,然后执行同步代码,依次反复,这个过程就叫做“事件循环”。

(2)、异步任务的类别:宏任务和微任务

宏任务:setInterval / setTimeout / setImmediate / I/O / ui render
微任务:new Promise() / async/await / new MutaionObserver()
宏任务和微任务的执行顺序:
主线程会先查看微任务队列是否有事件存在,如果有,先逐一执行,直到微任务全部执行完毕然后再去执行宏任务,如果没有,再去宏任务中取出事件放入到执行栈中执行。

你可能感兴趣的:(js,多线程,js,vue.js)