JavaScript 运行机制

JavaScript 中单线程的概念

所谓单线程指的是同一个时间只能做一件事。那么为什么如此设计呢?
JavaScript 中的单线程主要与这门语言的用途有关,作为浏览器脚本语言,JavaScript 主要用来与用户互动以及操作 DOM 元素,这一特性决定了它只能是单线程,如果它是多线程的那么会带来很多复杂的同步问题,比如有两个线程,一个线程在给某个 DOM 节点上添加内容,而另一个线程删除了这个 DOM 节点,此时该以哪个线程的操作为准?所以为了避免复杂性,JavaScript 只能是单线程。但为了利用多核 CPU 的计算能力,H5 提出 Web Worker 标准,允许 JavaScript 创建多个线程,但子线程完全受主线程控制且不得操作 DOM,因此这个新标准并没有改变 JavaScript 单线程的本质。

任务队列

由于 JavaScript 语言的单线程,所以所有任务都需要排队,必须等到前一个任务结束,才会执行后一个任务。但如果前一个任务耗时很长那么后一个任务就得一直等待,如果是因为前一个任务的计算量大 CPU 需要时间处理那么后一个任务的等待倒也可以接受,但有的时候是因为 IO 设备很慢,比如 常见的 Ajax 操作,需要从网络请求数据,不得不等待结果然后再往下执行。
所以 JavaScript 的设计者也意识到,这时主线程完全可以不管 IO 设备,将等待中的任务挂起,先执行后面的任务,等 IO 设备返回了结果,再回过头执行挂起的任务。于是所有的任务可以分成两种,一种是同步任务(synchronous),一种是异步任务(asynchronous)。同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕,后一个任务才能被执行;异步任务指的是不进入主线程,而进入任务队列(task queue)的任务。只有任务队列通知主线程某个任务可以执行了,该任务才会进入主线程。

异步执行的运行机制如下

  1. 所有同步任务都在主线程上执行,形成一个执行栈(excution context stack)
  2. 主线程之外存在一个任务队列(task queue),只要异步任务有了运行结果,就在任务队列之中放置一个事件
  3. 一旦执行栈中的所有同步任务执行完毕,就会读取任务队列,看看里面有哪些事件,那些对应的异步任务结束等待状态,进入执行栈开始执行
  4. 主线程不断重复上面的第三步

下图源自网络:

事件循环(Event Loop)

同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的 Event Loop(事件循环)。

事件和回调函数

任务队列中的事件,除了 IO 设备的事件以外,还包括一些用户产生的事件,比如鼠标点击,页面滚动等等,只要指定过回调函数,这些事件发生时就会进入任务队列,等待主线程读取。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

宏任务和微任务

宏任务(macrotask)和微任务(microtask)表示异步任务的两种分类。
宏任务包括:script(整体的代码块), setTimeout , setInterval , setImmediate , requestAnimationFranme;
微任务包括:promise.then , promise.catch , promise.finally , process.nextTick , MutationObserver;
执行顺序:
先执行同步代码,遇到异步宏任务将宏任务放入宏任务队列中,遇到异步微任务将微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后将宏任务调入主线程执行,一直循环直至所有任务执行完毕。

下图源自网络

一个小例子:

setTimeout(() => {
//执行后 回调一个宏事件
console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');

new Promise((resolve) => {
console.log('外层宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})

最后输出:
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3

posted @ 2020-12-09 15:26  秋裤里草木皆兵  阅读(120)  评论(0)    收藏  举报