浏览器事件循环Event Loop


JS的执行原理,事件循环

要了解事件循环,首先我们先了解进程和线程的概念。

  • 进程是操作系统运行中的程序。
  • 线程是操作系统能够调度的最小单位。
  • 一个进程可以包含多个线程,这些线程共享进程的内存和其他资源。
  • 每个进程中都会启动一个线程来执行代码,这个线程成为主线程

比如工厂是一个操作系统,车间相当于进程,车间里的工人相当于线程。进程相当于线程的容器。

目前的浏览器一般都是多进程的,一般开启一个tab页就会启动一个进程,防止一个页面的卡死造成所有页面无法响应。

js代码是在一个单独的线程中执行的,单线程统一时间只能干一件事,如果一段代码非常耗时,那么该线程会被阻塞。

在浏览器的事件循环中,主线程负责执行 同步任务,而异步任务是由任务队列处理。这种机制保证了异步任务可以在适当时机插入执行,从而实现了JavaScript的非阻塞异步执行。

事件循环流程如下:

  1. 主线程读取JavaScript代码,形成相应的堆和栈。
  2. 当主线程遇到异步任务时,将其委托给异步进程处理。
  3. 异步任务执行完成后,将响应的回调函数推入任务队列。
  4. 主线程执行完同步任务后,检查任务队列,如果有任务,则按照先进先出的原则将任务推入主线程执行。
  5. 重复执行上述步骤,知道任务队列为空。

同步任务

同步任务按照代码书写顺序一步一步执行的。当主线程执行同步任务时,会阻塞后续代码的执行,直到当前任务执行完成。包括函数、变量定义、算数运算、输出。如:

const a = 1;
console.log(a);
function fn() {}

异步任务

异步任务是在主线程执行的同时,通过回调函数或其他机制委托给其他线程或事件来处理的任务。在执行异步任务时,主线程不会等待任务完成,而是继续执行后续代码。异步任务包括:

  • 回调函数callback
  • Promise``async/await
  • Generator
  • 事件监听
  • 发布/订阅
  • 计时器
  • requestAnimationFrame
  • MutationObserver
  • process.nextTick
  • I/O操作
async function fn() {
	await request1();
   await request2();
}
setTimeout(() => {
	console.log(123)
}, 123)

任务队列

浏览器的事件循环维护两个队列:宏任务队列和微任务队列。JavaScript引擎遵循事件循环机制。在执行完当前的宏任务后,会检查微任务队列,执行其中的微任务,然后再取下一个宏任务执行。

1、 宏任务是一些较大粒度的任务,包括:

  • 所有同步任务
  • I/O操作
  • setTimeout``setInterval
  • requestAnimationFrame
  • 事件监听、回调函数等

2、 微任务是一些粒度较小的任务,包括:

  • Promisethencatchfinally
  • async/await
  • Generator
  • MutationObserver
  • process.nextTick

任务执行过程

  1. 同步任务在主线程排队执行,异步任务在事件队列排队进入主线下执行。
  2. 遇到宏任务则推进宏任务队列,遇到微任务则推进微任务队列。
  3. 执行宏任务,执行完毕后检查当前层的微任务并执行。
  4. 继续执行下一个宏任务,执行对应层次的微任务,直至所有任务执行完。

实例

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
})
promise1.then(() => {
  console.log(3);
});
console.log('1', promise1);

const fn = () => (new Promise((resolve, reject) => {
  console.log(2);
  resolve('success')
}))
fn().then(res => {
  console.log(res)
})
console.log('start')

文章作者: Icon
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Icon !
评论
  目录