什么是事件循环,还有宏任务、微任务、async和await,并进行流程分析

事件循环

事件循环是JavaScript运行时环境中的一种机制,用来处理异步任务和事件。负责管理代码的执行顺序,确保异步任务和事件能够被正确地处理,可以把事件循环看做一次无限循环,在每次循环中检查任务队列,执行队列中的任务,并处理事件。

举个通俗的例子🌰:

假设你是一名服务员,你的工作是不断接受顾客的订单,并将它们完成。但是你不能同时处理多个订单,你必须一次只能处理一个订单。那么你会如何处理呢?

首先,你会接受一个订单,然后按照顺序将订单完成。但是,如果有一个订单很复杂,需要花费很长时间才能完成,那么其他的订单就会等待。这种等待就好像 JavaScript 中的同步代码执行。

但是,如果你的工作是把订单交给另一个人处理,然后等待他处理完毕后再继续处理下一个订单呢?这样的话,你可以不必等待每个订单处理完毕,而是可以继续接受新的订单。这种处理方式就好像 JavaScript 中的异步代码执行。

在 JavaScript 中,事件循环就像是一个服务员,它不断地接受任务(或者称为事件),并按照特定的顺序执行它们。如果某个事件需要等待一些时间(例如,从服务器获取数据、执行动画等),事件循环就会继续执行下一个任务,而不会等待这个事件完成。

这就是事件循环的工作原理:它不断地循环遍历任务队列,检查是否有任务需要执行。如果有,就执行它;如果没有,就等待新的任务到来。

JavaScript 中的事件循环在浏览器和 Node.js 环境中都有。在浏览器中,事件循环负责处理 DOM 事件、Ajax 请求、定时器等任务;在 Node.js 环境中,事件循环负责处理 I/O 操作、文件读写、网络请求等任务。

宏任务

宏任务是指在事件循环中被放入到执行队列中的任务,例如 setTimeout、setInterval、ajax 请求、DOM 事件等。它们会在主线程执行,每次只执行一个宏任务,然后等待下一个循环。

常见的宏任务有:

  1. setTimeout 和 setInterval:用于设置定时任务,分别在一定的时间后执行一次或者定时循环执行。
  2. DOM 事件:例如 click、mouseover 等用户交互事件。
  3. XMLHttpRequest 和 Fetch:用于发送网络请求。
  4. History API 和 Location API:用于浏览器历史记录和地址栏操作。
  5. requestAnimationFrame:用于执行动画效果的刷新。
  6. 页面加载事件:如DOMContentLoaded、load 等。
  7. 文件读取操作:例如 FileReader、File API 等。
  8. Web Workers:用于在后台执行 JavaScript 代码的多线程环境。

微任务

微任务是在宏任务执行完毕后,在同一事件循环中立即执行的任务。微任务包括 Promise 的回调函数、async/await 等。微任务通常会在当前宏任务执行完毕后立即执行,然后执行下一个宏任务。

常见的微任务有:

  • process.nextTick

  • MutationObserver

  • Promise里的then、catch、finally

  • queueMicrotask(Node.js 中):Node.js 中的一个函数,用于添加微任务到微任务队列。

async和await

async/await 是 ES2017(也称为 ES8)中引入的异步编程语法,用于简化 Promise 的使用。async 函数用于定义一个返回 Promise 对象的异步函数,而 await 关键字用于暂停函数执行,等待 Promise 对象的解析。

流程分析

  1. 执行一个宏任务,同步代码是一个特殊的宏任务,(执行栈中没有就从任务队列中获取。执行栈:JavaScript是单线程的语言,它使用执行栈来管理代码的执行顺序,用来存储函数调用的上下文;任务队列:用来存储异步任务的队列);

  2. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中;

  3. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行);

  4. 当前宏任务执行完毕,开始检查渲染,然后渲染线程接管进行渲染;

  5. 渲染完毕后,JavaScript 线程继续接管,开始下一个循环。

举个例子🌰:

console.log(1);

setTimeout(() => console.log(2));

Promise.resolve().then(() => console.log(3));

Promise.resolve().then(() => setTimeout(() => console.log(4)));

Promise.resolve().then(() => console.log(5));

setTimeout(() => console.log(6));

console.log(7);

// 结果
/*
1 7 3 5 2 6 4
*/

再来一题:

Promise.resolve().then(() => {
    // 微任务1
    console.log('Promise1')
    setTimeout(() => {
        // 宏任务2
        console.log('setTimeout2')
    }, 0)
})
setTimeout(() => {
    // 宏任务1
    console.log('setTimeout1')
    Promise.resolve().then(() => {
        // 微任务2
        console.log('Promise2')
    })
}, 0)

// p1 s1 p2 s2

来一道复杂一点的:

console.log('stack [1]');
setTimeout(() => console.log("macro [2]"), 0);
setTimeout(() => console.log("macro [3]"), 1);

const p = Promise.resolve();
for(let i = 0; i < 3; i++) p.then(() => {
    setTimeout(() => {
        console.log('stack [4]')
        setTimeout(() => console.log("macro [5]"), 0);
        p.then(() => console.log('micro [6]'));
    }, 0);
    console.log("stack [7]");
});

console.log("macro [8]");

// 答案
stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4]
micro [6]
stack [4]
micro [6]
stack [4]
micro [6]

macro [5], macro [5], macro [5]

最后最难的一题(对我来说):

const $inner = document.querySelector('#inner')
const $outer = document.querySelector('#outer')

function handler () {
  console.log('click') // 直接输出

  Promise.resolve().then(_ => console.log('promise')) // 注册微任务

  setTimeout(() => console.log('timeout')) // 注册宏任务

  requestAnimationFrame(_ => console.log('animationFrame')) // 注册宏任务

  $outer.setAttribute('data-random', Math.random()) // DOM属性修改,触发微任务
}

new MutationObserver(_ => {
  console.log('observer')
}).observe($outer, {
  attributes: true
})

$inner.addEventListener('click', handler)
$outer.addEventListener('click', handler)

//答案:click -> promise -> observer -> click -> promise -> observer -> animationFrame -> animationFrame -> timeout -> timeout

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bonny雨曦

码字不易,多多鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值