JS执行原理大揭秘:事件循环Event Loop与宏任务、微任务

南木元元 2024-07-12 10:05:04 阅读 72

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元


目录

事件循环概述

异步和单线程

同步任务

异步任务

任务队列

宏任务

微任务

例题

示例1

示例2 

示例3 

示例4 

结语


事件循环概述

JavaScript是一种单线程语言,事件循环(Event Loop)作为JavaScript的核心执行机制,可以有效地进行异步处理,保证用户界面的响应性和流畅性。

事件循环的工作流程如下:

所有同步任务都在主线程上执行,形成一个执行栈在执行同步任务的时候,如果遇到了异步事件,会将该任务挂起,继续执行同步任务,当异步事件执行完后(如定时器到时,ajax请求返回),再将对应的回调加入到一个任务队列中等待执行,任务队列分为宏任务队列和微任务队列当执行栈中的同步任务执行完毕后,会执行所有微任务,清空微任务队列当执行完所有微任务后,再去执行宏任务队列中的下一个宏任务,不断循环,直到所有任务都完成。

异步和单线程

JavaScript 最初被设计为一门单线程语言,是因为它的主要用途是与用户的交互以及操作DOM。为了避免同一时间对同一个DOM元素进行操作从而导致不可预知的问题,JavaScript从一诞生就是单线程,避免了多线程环境中的复杂同步问题。

单线程的意思是在任何给定时间内,只能执行一个任务。这也意味着 JavaScript 代码执行可能面临阻塞问题,即一个耗时操作(如大量计算或高延迟的 I/O 操作)会阻塞整个程序的运行,如果代码阻塞只能一直等下去,这样导致很差的用户体验。所以事件循环的出现让 js 拥有异步的能力。

同步任务

同步任务指的是在主线程上直接执行的任务,按照代码顺序依次执行,其执行会阻塞后续代码的运行,直到任务完成。

console.log('开始执行');

// 同步任务

function doSomething() {

console.log('这是一个同步任务');

}

doSomething(); // 调用同步函数

console.log('结束执行');

//开始执行

//这是一个同步任务

//结束执行

在这个示例中, doSomething()函数的调用是同步的,因此它会立即执行,并且后续代码必须等待它执行完成才能继续。

异步任务

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

异步执行的机制使得 JavaScript 能够更好地处理耗时操作,保持页面的响应性。

console.log('开始执行');

// 异步任务

setTimeout(() => {

console.log('这是一个异步任务');

}, 1000); // 延迟1秒后执行

console.log('结束执行');

//开始执行

//结束执行

//这是一个异步任务

在上述例子中,setTimeout是一个异步任务,它会在1秒后将回调函数推入任务队列,而主线程不会等待这个1秒,而是继续执行后面的console.log('结束执行')。当主线程的同步任务执行完成后,它会检查任务队列,将异步任务的回调函数推入执行栈进行执行。

任务队列

任务队列分为宏任务队列和微任务队列两种。每当一个宏任务执行完毕,会检查微任务队列,执行所有的微任务,再继续下一个宏任务。

宏任务

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

scriptsetTimeout、setInterval

I/O操作,如文件读写

用户交互事件,如点击事件

UI render(页面渲染)

requestAnimationFrame(在浏览器中)

...

微任务

微任务通常是需要快速执行的小任务,它们的执行时机是在当前宏任务执行完毕并且在下一个宏任务开始之前。微任务的执行会比任何其他宏任务以及渲染操作更优先。包括:

Promise.thenasync/awaitprocess.nextTick(node中)...

为什么要引入微任务?

为了解决异步回调的问题,如果将异步回调也进行宏任务的入队操作,那么必须等前面所有的宏任务完成之后再执行回调,如果任务队列非常长,那么回调迟迟得不到执行,就会造成页面卡顿。通过引入微任务的方式,在每一个宏任务中定义一个微任务队列,当该宏任务执行完成,会先去执行其中所有微任务,执行完成才去执行下一个宏任务。

概况一下:为了给高优先级任务一个插队的机会,否则新入队的任务永远被放在队尾,微任务比宏任务有更高优先级。

例题

如果看到这里,还觉得有点懵,那么接下来就通过几个例子来帮助你更好地理解。

示例1

创建Promise实例是同步的,所以先执行主线程上的同步任务,打印1、3、4、5、7。主线程的同步任务执行完毕后,会先执行微任务,执行Promise的then方法里的代码,打印6微任务执行完毕后,最后执行定时器里的宏任务,打印2

示例2 

注意点:如果在执行微任务的过程中,产生新的微任务也需要一起清空;微任务队列没清空之前,是不会执行下一个宏任务的。

示例3 

注意点:事件循环每一次只执行一个宏任务

示例4 

注意点:任务是DOM渲染前触发,宏任务是DOM渲染后触发

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。