JS是单线程语言
JS是一种单线程(single thread)语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,从上往下一行一行进行执行,否则会带来很复杂的同步问题。
- JS是单线程语言,只能同时做一件事儿
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
- JS和DOM渲染共用同一个线程,因为JS可修改DOM结构
- DOM事件也使用回调,基于event loop
Event loop
由图所知 event loop 是由 JS RunTime(主线程)、Web Apis (外部API)以及 Callback Queue(任务队列) 组成的。
- stack:用于分配原始类型
- heap:用于分配对象类型
- queue:先进先出,排在前面的事件,优先被处理。
同步代码,一行一行放在主线程执行,会将不同类型的数据保存到堆和栈中。遇到异步也就是调用各种不同的Web Apis,会先“记录”下,等待时机(定时、网络请求等)。时机到了,就添加到到Callback Queue。如主线程为空(即同步代码执行完)Event Loop开始工作,循环查找Callback Queue,如有则移动到主线程执行,然后继续循环查找。
宏任务微任务
任务队列有宏任务和微任务之分:
宏任务队列(macro task queue):script(整体代码),ajax、setTimeout、setInterval、DOM监听、UI Rendering 等
微任务队列(micro task queue):Promise 的 then 回调、 Mutation Observer API、queueMicrotask() 等
事件循环执行的优先级:主程序 —> 微任务 —> 宏任务
1、main script 中的代码优先执行(编写的顶层script代码);
2、先执行微任务,当微任务队列为空后再执行宏任务
举个例子
async function async1 () {
console.log('async1 start')
await async2();
console.log('async1 end') // 加入到微任务队列
}
async function async2 () {
// return undefined --> new Promise,因为async 函数返回一定是个 promise
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise (function (resolve) {
console.log('promise1')
resolve();
}).then (function () {
console.log('promise2')
})
console.log('script end')
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
先执行 script (整体代码),第一步执行console.log('script start')
,asycn1()
函数和async2()
函数只是已定义但还没声明,所以先不管它们。console.log('script start')
执行完后将跳出函数执行栈。
遇到了setTimeout
函数,进入marco task 队列:
然后执行async1()
函数,打印 'async1 start'
将 await 后面的代码 console.log('async1 end')
加入到 micro task队列中
执行async2()
函数,打印 'async2'
后再执行 promise 函数
再执行promise函数后会打印 promise1
,后 promise 状态转变为 resolve,将console.log('promise2')
加入micro task 队列。最后再执行 console.log('script end')
至此主程序执行完毕,就先执行micro task 队列里的函数,主程序执行完毕后,再执行macro task 队列里的函数。
PS:零延迟
当 setTimeout 函数的 delay 为零的时候并不意味着回调会立即执行。其等待的时间取决于队列里待处理的消息数量。因为只有队列里的所有任务都处理完后才会处理 setTimeout 函数。
Reference
JavaScript 运行机制详解:再谈Event Loop - 阮一峰的网络日志 (ruanyifeng.com)