事件循环
JS所有任务分为两种
- 同步任务
- 异步任务
同步任务
调用立即得到结果的任务,同步任务在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步任务
调用无法立即得到结果,需要通过一系列的额外操作才能得到结果的任务
异步任务不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行
异步执行的运行机制:
- 所有同步任务都在主线程上执行,形成一个【执行栈】
- 主线程之外,还存在一个【任务队列】(task queue)。只要异步任务有了运行结果,就在"任务队列"中回调一个事件(不是全部异步任务都要回调函数)
- 一旦【执行栈】中的所有同步任务执行完毕,系统就会读【任务队列】。对应第一个的异步任务结束等待状态,进入【执行栈】,开始执行
- 主线程不断重复上面的第三步

JavaScript引擎是基于事件驱动的
宏任务(task)
-
JS引擎会在一个 task 执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->...)
-
eg:鼠标点击会触发一个事件回调、setTimeout、setInterval

微任务(microtasks)
- 需要在当前 task 执行结束后立即执行的任务,比如对一系列动作做出反馈,或者需要异步的执行任务而又不需要分配一个新的 task,这样便可以减小一点性能的开销
- 在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行
- eg:mutation observe的回调、promise
pormise
- pormise 新建后就会立即执行
- pormise有了结果,或者早已有了结果(有了结果是指这个promise到了fulfilled或rejected状态),他就会为它的回调产生一个微任务
const observer = new MutationObserver((mutations, observer) => {
console.log(mutations);//[{type: 'childList',.....}]
})
observer.observe(root, {
childList: true,
subtree: true
})
const p = Promise.resolve();
root.appendChild(document.createElement('div'));
// DOM操作执行完成,触发观察回调
p.then(() => {
console.log('reslove')
})
// 运行结果 👇
reslove
[{type: .......}]
异步任务定时器
console.log('start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
console.log('end');
👇
console.log
start
end
setTimeout
注意:即便主线程为空,0ms实际上也是达不到的,根据HTML标准,最低是4ms
setTimeout 与setInterval
setTimeout(function(){
console.log('a')
setTimeout(arguments.callee,1000)
},1000)
setInterval(function(){
console.log('a')
},1000)
//setTimeout 的运行过程👇
0s 开始执行 1s 2s 3s
|___/_______|___/_______|__________|__________|
0.3s👆 运行完 相隔1s 1.3s开始第二次
//setInterval 的运行过程👇 每隔1s就添加事件到队列
0s 开始执行 1s 2s 3s
|___/_______|___/_______|__________|__________|
0.3s👆 运行完 1s开始第二次
// 可以看出,setInterval从执行完成到执行下一次定时器的时间间隔小于1s
// 如果没有执行完的,会定时器跳过,定时器会保证执行时前面已经没有定时器事件
// setInterval 有可能多个定时器扎堆
延时执行时间有最大值
Chrome、Safari、Firefox 都是以 32 个 bit 来存储延时值的,32bit 最大只能存放的数字是 2147483647 毫秒,这就意味着,如果 setTimeout 设置的延迟值大于 2147483647 毫秒(大约 24.8 天)时就会溢出,这导致定时器会被立即执行
let startTime = Date.now()
function foo(){
const endTime = Date.now()
console.log('cost time',endTime - startTime)
console.log("test")
}
var timerID = setTimeout(foo,2147483648);//会被立即调用执行
【多多登场】
