JS执行机制
一 单线程
- JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事
- JavaScript作为浏览器脚本语言,主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题
- 尽管为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质
二 任务类型
流程图
说明
- 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入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并执行。
三 队列类型
原理图
具体说明
- JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队
- 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调、DOM 事件回调、ajax 回调
- 微列队:用来保存待执行的微任务(回调),比如:promise的回调、MutationObserver 的回调
- JS 执行时会区别这 2 个队列
- JS 引擎首先必须先执行所有的初始化同步任务代码
- 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行
优先级
常见分类
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);