目录
单线程
同步和异步
执行过程
前言: 为什么要了解事件循环(evemtloop)呢?这样可以让我们对底层的代码运行机制有一个更清晰的认知了解,扎实我们的基本功。明白了事件循环后,于我们而言对后续的同步异步、Promise、Vue的$nextTick等等的学习都会有很好的理解与帮助。
单线程
在说事件循环之前,我们要先知道,javascript不同于其他的多线程语言。javascript是单线程的,也就是说同一个时间内呢,它只能做一件事,这与javascript本身的特性有关,因为js是浏览器脚本语言,它更多的是要需要用户进行交互、还有操作DOM等等。这些便决定了它只能单线程。
比如以下有两个线程
线 程 1 -- 添 加 节 点 DOM 1 | 线 程 2 -- 删 除 节 点 DOM 1 |
如果去执行的话,很明显这两个不能同时执行,肯定等到添加完 DOM1 后才能去删除,所以这也是js的语言特点所在。因此它的执行顺序应该是以下这种。
线 程 1 | 添 加 节 点 DOM 1 |
线 程 2 | 删 除 节 点 DOM 1 |
当然,这样的单线程也是会引起一些问题的
比如以下情况 :
有些任务是耗时的,亦或者有些任务需要执行某些事件执行后才能去执行,那么这样就会阻塞了代码执行,这样去等着肯定是有问题的。
同步和异步
有问题就需要去解决。所以js便引出了同步和异步的概念。这时在同步代码和异步代码之间就会有鲜明的执行顺序了。
什么是同步和异步呢?
同步就是后一个任务等待前一个任务执行完成后,再执行,执行顺序和任务的排序顺序一样
异步是非阻塞的,异步逻辑与主逻辑相互独立,主逻辑不需要等待异步逻辑完成,而是阔以直接执行下去
同步代码有:
比如 console.log( “ Hello Word” ) 这种的就属于立即执行的同步代码
异步代码有:
setInterval(定时器)
setTimeout(一次性定时器)
AJAX/Fetch
事件绑定
promise实例的.then方法( Promise本身是同步的 )
可以发现,异步代码有一个共同点,那就是比较耗时。
在 JS 中,异步任务还会分为宏任务和微任务。
因为在ES5之后,JS又引入了Promise。这样,promise不需要浏览器,JS引擎 自身可以发起异步任务了。
所以在执行过程中,我们还可以把JS代码分为三类
1. 同步代码(JS执行栈 / 回调栈)
2. 微任务的异步代码(JS引擎)
process.nextTick( node )
Promise.then( ).catch( )
Async/Await
Object.observe等等
3. 宏任务中的异步代码 (宿主环境)
Script( 整个代码块 )
setInterval/setTimeout 定时器
执行过程
先给大家说一个适合比较直观的可以练习和理解事件循环的网站
点击进入 : JS Visualizer 9000
那么事件循环究竟是怎样的一个过程呢?
废话不多说,看图:
js在解析代码时,当遇到同步代码,会立即放进 JS引擎(主线程) 中执行,并且原地等待结果。遇到异步代码时,会先放入 宿主环境(浏览器/Node) ,因为JS引擎是无法处理异步的,它会放到浏览器或者node去处理, 不需要原地等待结果,不会阻塞主线程向下执行代码,异步结果会在将来执行。当正确的时机到来后(比如定时器时间到了),宿主环境会把里面的异步代码的回调函数推送到一个任务队列(此时会分为宏任务和微任务队列,优先执行微任务队列里的异步任务),所以异步任务其实是会推到一个任务队列中来排队的,因为可能会有多个不同执行时间的异步任务。如果是一个点击事件,会在点击完成后才会将宿主环境中的回调函数推到任务队列中。这个时候,如果执行栈中的同步任务全部执行完成后,就会来到任务队列中查看是否有异步任务,有就推送到执行栈中去执行,执行完后再到任务队列中看有没有异步任务,有的话就再推过去执行,来来回回反复的过程我们称为事件循环。
下面阔以看下简洁版的