JS执行机制

一 单线程

  • JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事
  • JavaScript作为浏览器脚本语言,主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题
  • 尽管为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质

二 任务类型

  • 任务分为:同步任务和异步任务

流程图

eventloop

说明

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue
  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)
  • 补充:js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数

示例

$.ajax({
    url: url,
    data:{},
    success:() => {
        console.log('发送成功!');
    }
})
console.log('代码执行结束');
  • ajax进入Event Table,注册回调函数success。
  • 执行console.log('代码执行结束')。
  • ajax事件完成,回调函数success进入Event Queue。
  • 主线程从Event Queue读取回调函数success并执行。

三 队列类型

  • 异步任务-也叫异步队列分为:宏队列和微队列

原理图

eventloop

具体说明

  • JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队
    1. 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调、DOM 事件回调、ajax 回调
    2. 微列队:用来保存待执行的微任务(回调),比如:promise的回调、MutationObserver 的回调
  • JS 执行时会区别这 2 个队列
    1. JS 引擎首先必须先执行所有的初始化同步任务代码
    2. 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行

优先级

同步任务  >  微任务  >  宏任务

常见分类

  • 宏任务
script(整体代码)
setTimeout
setInterval
setImmediate
I/O 操作等
requestAnimationFrame
  • 微任务
process.nextTick
new Promise().then(回调) | catch | finally
MutationObserver

四 测试题

console.log(1);
async function async () {
    console.log(2);
    await console.log(3);
	console.log(4)
	await async2();
	console.log(5)
}

async function async2() {
	console.log(6);
}

setTimeout(() => {
	console.log(7);
}, 0);

const promise = new Promise((resolve, reject) => {
    console.log(8);
    resolve(9)
})
promise.then(res => {
	console.log(res)
})

async(); 
console.log(10);

// 1 8 2 3 10 9 4 6 5 7