下面是Chrome 浏览器的进程架构图:


每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理html,又要处理css、js 以及各种事件,如何做到有条不紊地执行任务呢,需要事件循环来帮助统筹调度。

事件循环的两个基本原则:
渲染进程会有个队列专门存放待执行的任务 ,队列中会有输入事件(鼠标滚动、点击、移动)、dom解析、样式计算,微任务、计时器等等类型,队列有着先进先出的特点,主线程会从队列中那个循环取出任务执行
微任务是需要尽可能快的执行,微任务使得我们在渲染ui之前执行指定的行为,避免不必要的ui重绘,如promise既是典型的微任务。
在一次循环中,事件循环首先检查宏任务队列,如有任务在等待则立即开始执行任务,直到执行完成,事件循环将检查微任务队列,如有任务在等待,则事件循环会一次开始执行微任务,直到微任务队列被清空。单次循环中,最多处理一个宏任务,而队列中的微任务都会被处理。当处理完成后,事件循环会检查是否需要更新ui渲染,如果是则会重新渲染ui视图。

Q1:以下代码什么时候打印 “2022年” 呢?2147483648=大约 24.8 天
function showName(){ console.log("2022年")};
var timerID = setTimeout(showName,2147483648);
setTimeout(()=>{
//to do something runs for 6ms
},10)
setInterval(()=>{
//to do something runs for 8ms
},10)
const myButton=document.getElementById("myButton")
myButton.addEventListener('click',function(){
//to do something runs for 10ms
})
// to do something runs for 18ms
示例中发生顺序如下
1、0ms时,setTimeout 延迟10ms 执行,setInterval 延迟10ms 执行
2、6ms 时点击了按钮
3、10ms时,setTimeout到期,setInterval 第一个时间间隔触发
间隔计时器每10ms 触发一次 ,则间隔时间是
10、20、30、40、50、60、70
时间轴如下:

| 宏任务队列 | 操作 |
|---|---|
| 0ms | 执行js主线程 |
| 6ms | 添加单击事件进入队列 |
| 10ms | 两个计时器被添加入队列 |

例子中的间隔计时器尽管我们的预期是 10、20、30、40、50、60、70时触发,事时上回调函数却在34、42、50、60、70ms时执行,少执行了两次,
由此可见,我们只能控制计时器何时被加入队列而无法控制何时执行,计时器上的延迟时间只代表最少延时时间,真正的延迟时间视任务队列情况而定。
回到刚才的问题:
function showName(){ console.log("2022年")};
var timerID = setTimeout(showName,2147483648);
1、解:
Chrome、Safari、Firefox 都是以 32 个 bit 来存储延时值的,32bit 最大只能存放的数字是 2^31-1 = 2147483647 毫秒,这就意味着,如果 setTimeout 设置的延迟值大于 2147483647 毫秒(大约 24.8 天)时就会溢出,那么相当于延时值被设置为 0 了,这导致定时器会被立即执行

| 十进制 | 二进制 | 反码 | 补码 |
|---|---|---|---|
| 1 | 00000001 | 00000001 | 00000001 |
| -1 | 10000001 | 11111110 | 11111111 |
