彻底理解事件循环(Event Loop)

同步和异步

  • 同步任务立即执行的任务在主线程上排队执行,形成一个执行栈,只有前一个任务执行完毕,才能继续执行下一个任务

  • 同步代码:是指在程序中按照顺序执行的代码,前一个操作必须完成后才能进行下一个操作。每一行代码会等待前面的代码执行完毕后再继续往下执行

  • 异步任务不进入主线程,进入任务队列(宏任务队列和微任务队列)的任务;只有等主线程任务全部执行完毕,任务队列的任务才会进入主线程执行

  • 异步代码:是指不按照代码的书写顺序执行的代码。它可以在执行某个操作的过程中,不必等待这个操作完成,就可以继续执行后面的代码。当异步操作完成时,会通过回调函数、Promise 等方式通知程序,触发下一步操作

进程和线程

进程是线程的容器

  • 进程:启动一个应用程序,就会默认启动一个进程(也可能是多个进程)
  • 线程:每一个进程中,都会启动至少一个线程用来执行程序中的代码,这个线程被称之为主线程
  • 例子解释
    • 操作系统类似于一个大工厂,工厂中里有很多车间,这个车间就是进程,每个车间可能有一个以上的工人在工厂,这个工人就是线程
      在这里插入图片描述

进程

  • 进程是操作系统中运行程序的基本单位,一个进程可以被看作是程序执行的实例

  • 它拥有自己独立的内存空间、系统资源(文件句柄、网络连接等),并且能够执行任务

  • 当你启动一个应用程序(如浏览器、文本编辑器等),操作系统就会为这个应用程序创建一个进程

线程

  • 线程是进程中的一个执行单元,它是 CPU 调度的最小单位

  • 一个进程可以包含多个线程,多个线程共享进程的资源(如内存、文件句柄等)

  • 每个线程有自己的栈空间和程序计数器

浏览器中的JavaScript线程

经常说JavaScript是单线程(可以开启workers)的,但是JavaScript的线程有自己的容器进程:浏览器或者Node

浏览器是一个进程吗?它里面只有一个线程吗?

  • 目前多数的浏览器其实都是多进程的,当我们打开一个tab页面时就会开启一个新的进程

  • 为了防止一个页面卡死而造成所有页面无法响应

  • 每个进程中又有很多的线程,其中包括执行JavaScript代码的线程

JavaScript的代码执行是在一个单独的线程中执行的

  • JavaScript的代码,在同一个时刻只能做一件事

  • 虽然 JavaScript 是单线程的,但许多耗时操作(如网络请求、文件操作、定时器等)是由浏览器或 Node.js 底层的多线程机制处理的

  • JavaScript 主线程只负责逻辑控制,实际的耗时任务被分配给后台的其他线程,最终再回到主线程进行后续处理

  • 这种机制避免了阻塞,保证了 JavaScript 的非阻塞、异步执行能力

事件循环

通过学习上面浏览器中的JavaScript线程知识,我们知道了 JavaScript 是单线程的,但它通过分配其他线程来实现异步操作最后再回到主线程处理,从而避免了阻塞操作,这种机制就是事件循环

事件循环(Event Loop)是 JavaScript 执行异步代码的核心机制(这里主要学习浏览器的事件循环,暂时不学Node的事件循环)

在这里插入图片描述

那么这个过程是怎么操作的?继续往后学习↓↓↓

宏任务和微任务

这些耗时的任务被放在哪里了,执行顺序又是什么样的呐?

我们必须先要理解两个概念宏任务(Macrotask)和微任务(Microtask)是 JavaScript 事件循环中两类异步任务,它们的执行顺序和优先级决定了异步代码的执行时机

宏任务

  • 宏任务是指一些较大的异步操作,常见宏任务包括
    • 每个 <script> 标签中的代码块都是独立的宏任务

    • ajax(网络请求)

    • setTimeout

    • setInterval

    • DOM监听(如点击滚动事件)

    • UI RenderingUI渲染)

微任务

  • 微任务是指一些比宏任务更小、更细粒度的异步操作,常见微任务包括
    • Promisethen回调

    • await 后面跟promise或异步函数(也是promise)时,await 后面的代码和前面的赋值也会被放入微任务队列

    • Mutation Observer API(监听DOM变化)

    • queueMicrotask()

    • process.nextTickNode.js 中)

总结

放在哪里:

  • 这些宏任务和微任务都放在了各自的队列中,遵循先进先出(FIFO) 的原则

执行顺序:

  • 微任务的执行优先级比宏任务更高在一个微任务执行完毕后JavaScript 引擎会继续检查是否还有其他微任务,而不是立即切换到宏任务队列

  • 在执行任何一个宏任务之前,都会先查看微任务队列中是否有任务需要执行

  • 也就是宏任务执行之前,必须保证微任务队列是空的

  • 如果不为空,那么就优先执行微任务队列中的任务(回调)

代码练习题

上面看了太多云里雾里的理论知识,我们下面做下练习题让整个知识体系串联起来

练习一

console.log('script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)

Promise.resolve()
  .then(function () {
    console.log('promise1')
  })
  .then(function () {
    console.log('promise2')
  })

console.log('script end')

事件循环的过程解释如下:
在这里插入图片描述

练习二

setTimeout(function () {
  console.log('timer1')
}, 10)

setTimeout(function () {
  console.log('timer2')
}, 0)

new Promise(function executor(resolve) {
  console.log('promise 1')
  resolve()
  console.log('promise 2')
}).then(function () {
  console.log('promise then')
})

console.log('end')

事件循环的过程解释如下:
在这里插入图片描述

练习三

setTimeout(function () {
  console.log("setTimeout1");
  
  new Promise(function (resolve) {
    resolve();
  }).then(function () {
    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then4");
    });
    console.log("then2");
  });
});

new Promise(function (resolve) {
  console.log("promise1");
  resolve();
}).then(function () {
  console.log("then1");
});

setTimeout(() => {
  console.log("setTimeout2");
});

console.log(2);

queueMicrotask(() => {
  console.log("queueMicrotask1");
});

new Promise(function (resolve) {
  resolve();
}).then(function () {
  console.log("then3");
});

事件循环过程解释如下:
在这里插入图片描述

练习四

async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}
async function async2() {
  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')

事件循环过程解释如下:
在这里插入图片描述

练习五

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  new Promise(function (resolve) {
    console.log('promise1');
    resolve();
  }).then(function () {
    console.log('promise2');
  });
}

console.log('script start');

setTimeout(function () {
  console.log('setTimeout');
}, 0)

async1();

new Promise(function (resolve) {
  console.log('promise3');
  resolve();
}).then(function () {
  console.log('promise4');
});

console.log('script end');

事件循环过程解释如下:
在这里插入图片描述

练习六

async function async1() {
  console.log('async1 start');
  await async2();
  setTimeout(function() {
    console.log('setTimeout1') 
  }, 20)
  console.log("async1 end")
}

async function async2() {
  setTimeout(function() {
    console.log('setTimeout2')
  }, 0)
}

console.log('script start');

setTimeout(function() {
  console.log('setTimeout3');
}, 10)

async1();

new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});

console.log('script end');

事件循环过程解释如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值