Event loop/浏览器的事件循环机制

一、Event loop引入

1、js为什么是单线程的?

        之前我们已经学习了浏览器的关键渲染路径,知道了网页内容交给浏览器后浏览器是如何解析、渲染、绘制的,也提到了浏览器是多线程的,js是单线程的,那么为什么js就是单线程的呢?通过反证法如果js是多线程,多个js任务同时更新同一个dom,一个删除一个修改,这时候浏览器就不知道该如何执行了,故js是单线程的。

2、js为什么需要异步任务?

        如果 JavaScript 中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验,所以 JavaScript 中存在异步执行。

3、什么是Event loop

        既然js是单选程的,他要如何执行异步任务呢?因此浏览器通过事件循环机制来解决。那么什么是事件循环机制呢?事件循环的概念非常简单。有一个无休止的循环,JavaScript引擎等待任务,执行任务,然后休眠,等待更多任务。接下来我们以浏览器执行js代码为例,讲解什么是事件循环。

二、Event loop

1、总览

①JS本身是没有办法执行异步任务的,需要环境的支持(如浏览器)

②JS有两个任务队列(宏任务和微任务)和一个任务执行栈,执行栈只能放一个宏任务或者微任务,执行完这个任务后清空执行栈。

③当主线程中js代码全部执行完毕后,这时js解析器会从任务队列中取异步任务来进行执行,根据优先级先取出宏任务执行(一次只取一个执行),执行完毕后,去执行所有的微任务,然后再去取下一个宏任务完成后再去取所有微任务,重复执行上述操作,这种机制就是事件循环。

2、常见的微任务和宏任务

  • 常见的微任务包括: promise 的回调、await(本质也是Promise)、node 中的 process.nextTick 、对 Dom 变化监听的 MutationObserver。
  • 常见的宏任务包括: script 脚本的执行、setTimeout ,setInterval ,setImmediate 一类的定时事件,还有如 I/O 操作、UI 渲染等。

3、代码演示

<script>
  function fn(){
    console.log(1);

    setTimeout(() => {
      console.log(2);
      Promise.resolve().then(() => {
        console.log(3)
      });
    });

    new Promise((resolve, reject) => {
      console.log(4)
      resolve(5)
    }).then((data) => {
      console.log(data);

      Promise.resolve().then(() => {
        console.log(6)
      }).then(() => {
        console.log(7)

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

    setTimeout(() => {
      console.log(9);
    })

    console.log(10);
  }
fn();
<script>

①第一次事件循环

  • script入宏队列

MacroTask Queue(宏任务)

MicroTask Queue(微任务)

输出

<script>....</script>

  • script入栈

MacroTask Queue(宏任务)

MicroTask Queue(微任务)

Call Stack

<script>....</script>

  • 开始执行同步代码

步骤①: console.log(1);

步骤②: setTimeout入宏队列

步骤③: 执行promise构造函数,promise.then()之后的代码入微任务队列

步骤④: setTimeout入宏队列

步骤⑤:console.log(10);

MacroTask Queue(宏任务)

MicroTask Queue(微任务)

输出

setTimeout(() => {
   console.log(2);
   Promise.resolve().then(() => {
   console.log(3)
   });
});
setTimeout(() => {
  console.log(9);
})

then((data) => {
    console.log(data);
    Promise.resolve().then(() => {
	console.log(6)
    }).then(() => {
	console.log(7)
	setTimeout(() => {
	   console.log(8)
	 }, 0);
     });
})

1

4

10

  • 执行微任务1

步骤①:console.log(data);//data为5

步骤②: 执行promise构造函数,promise.then()之后的代码入微任务队列

MacroTask Queue(宏任务)

MicroTask Queue(微任务)

输出

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
});
setTimeout(() => {
  console.log(9);
})

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

1

4

10

5

6

  • 继续执行微队列

步骤①:console.log(7)

步骤②:setTimeout入宏队列

MacroTask Queue(宏任务)

MicroTask Queue(微任务)

输出

setTimeout(() => {
  console.log(2);
  Promise.resolve().then(() => {
    console.log(3)
  });
}); 
setTimeout(() => {
  console.log(9);
})
setTimeout(() => {
  console.log(8)
}, 0)

1

4

10

5

6

7

至此第一遍事件循环结束剩下的需要你自己分析喽!

根据分析输出结果为:

输出

1

4

10

5

6

7

2

3

9

8

完结撒花!

参考文章:

【1】宏任务和微任务_了不起的小瑜儿的博客-CSDN博客基本概念介绍同步异步事件循环机制常见的微任务和宏任务https://blog.csdn.net/qq_43057018/article/details/125128909

【2】浏览器中JS单线程机制与异步的实现_hehaonan~的博客-CSDN博客本文主要讨论浏览器环境上的js单线程机制和js异步的实现,关于其他环境下,比如node就暂时不讨论现来说结论:JS本身是没有办法真正实现异步的,异步的实现需要环境的支持(比如浏览器,因为浏览器是多线程的)。JS有两个任务队列,分别是宏任务(macrotasks)队列和微任务(microtask)队列,JS也只有一个任务执行栈,执行栈只能放一个宏任务或一个微任务,执行完这个任务后清空执行栈。浏览器有一个event table,用来确定宏任务或者微任务何时被添加到相应队列的队尾(个人觉得这个才是https://blog.csdn.net/m0_51218245/article/details/121053965

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值