Javascript中的事件循环机制详解

目录

概述

一、进程和线程

1.进程(Process):

2.线程(Thread):

二、Javascript单线程解释

三、JavaScript事件循环(Event Loop)

列举以下几种(非全部)

总结


概述

事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。
JavaScript中的事件循环是一种异步执行机制,作用是协调和管理各种异步任务的执行顺序,以确保JavaScript代码执行的顺序和预期一致。
代码在执行时会先执行同步任务,而异步任务则被放入任务队列中等待执行。事件的循环机制会不断地检查任务队列中是否有任务需要执行,如果有,则将任务取出并执行。在执行异步任务时,引擎会挂起当前任务的执行,去执行异步任务,等到异步任务执行完成后,再回到之前的任务继续执行。

事件循环的组成:调用栈、任务队列和事件循环线程。调用栈用来管理代码的执行顺序,任务队列用来存放异步任务,事件循环线程则是一个循环,不断地从任务队列中取出任务执行。

一、进程和线程

在将JavaScript事件循环前先简单了解一下进程和线程

1.进程(Process):

在系统中程序运行需要有它自身的独立内存空间,可以把分配给程序运行的这块内存空间理解为进程。进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
每个应用最少需要一个进程,进程与进程之间是独立的存在,即使需要通讯,也需要双方建立关系(同意相互之间通讯)


2.线程(Thread):


线程是系统中能够进行运算的最小单位,一个进程最少有一个线程,所以在进程开启后会主动创建一个线程来运行代码程序,该线程称之为主线程,如果程序代码中需要同时执行多个代码块的话,主线程就会同时开启多个线程来执行,因此一个进程中是可以包含多个线程的。

简单常见的示例,启动浏览器后就会开启多个进程,如浏览器、网络进程、渲染进程等,且浏览器的每一个新的标签页都是独立的进程,而每个进程下都有一个线程,所以说浏览器是一个多进程和多线程的应用。

二、Javascript单线程解释


单线程就意味着,所有任务需要排队,前面的任务结束,才会执行后面的任务。假如一个任务耗时很长,后面的一直等着,就会造成阻塞,不得不等着结果出来,再往下执行。

因为,在设计之初,JavaScript就是单线程,这是Javascript这门语言的核心特征。

为了解决这一问题,Javascript把任务分成两种,一种是同步任务,一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入任务队列中的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。


所以Javascript是单线程代码执行不阻塞主进程一种机制。

Javascript作为浏览器脚本语言,它的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。如果JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器就无法确定以哪个线程为准了。


在HTML5中提出Web Worker标准
Web Worker 是一种浏览器提供的 JavaScript API,它允许在后台线程中运行脚本,而不会阻塞主线程。这意味着,即使脚本执行了很长时间,Web 应用程序的 UI 仍然可以保持响应。

Web Worker 有两种类型:Dedicated Worker 和 Shared Worker。Dedicated Worker 是指与一个页面绑定的 Worker,它仅能由该页面的脚本使用。而 Shared Worker 则可以被多个页面共享使用,这使得多个页面可以同时访问同一个后台线程

即使HTML5中允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。


三、JavaScript事件循环(Event Loop)

Javascript开始执行的时候,会分为两部分进行执行,同步和异步

1、同步任务会直接进入主线程依次执行;
2、异步任务会再分为宏任务和微任务。


当在JavaScript代码执行时,如果遇到一个异步任务,如 setTimeout()、Promise,则会将任务添加到任务队列中,等待执行。在JavaScript引擎空闲时,事件循环线程会不断地从任务队列中取出任务,放入调用栈中执行,直到任务队列为空为止。

当主线程内的任务执行完毕,优先检查微任务的任务队列,如果有任务,就进入主线程进行依次执行,没有就从宏任务队列读取下一个宏任务执行;
每执行完一个宏任务就清空一次微任务队列,不断重复,这就是事件的循环;

来看一个实例

function bobEventLoop(){
    console.log("1");
    setTimeout(()=>{
        console.log("2");    
    },0);
    console.log("3");
}
bobEventLoop();//输出的结果  1、3、2

通过以上代码可以看出,首先执行完了主任务后再执行了异步任务,因为setTimeout是异步任务,所以等主任务执行完了才会执行setTimeout里面的异步任务接下来再使用Promise来创建一个异步任务,看下列实例

function bobEventLoop(){
    console.log("1");
    setTimeout(()=>{
        console.log("2");    
    },0);

    new Promise((resolve, reject)=>{
        console.log('3');
        setTimeout(() => {
            console.log('4');
        }, 0);
        resolve();
    }).then(()=>{
        console.log('5')
    })
    console.log("6");
}
bobEventLoop();//输出的结果  1、3、6、5、2、4

从以上可以new Promise()里面的函数为同步任务

function bobEventLoop(){
    console.log("1");
    setTimeout(()=>{
        console.log("2");    
    },0);
    Promise.resolve().then(()=>{
        console.log('3');
        setTimeout(() => {
            console.log('4');
        }, 0);
    });
    console.log("5");
}
bobEventLoop(); //输出为1、5、3、2、4

因为异步任务可以分为两种,一种是微任务,一种是宏任务,通过以上实例可以看出Promise创建的异步任务优先于setTimeout创建的异步任务,这也说明了微任务的执行是优先于宏任务的执行

列举以下几种(非全部)

宏任务的创建方法有
setTimeout
setInterval
ajax
事件
.....

微任务的创建有
new Promise()后的then与catch函数
async
await

总结

当第一个宏任务进入主线程,进行第一次事件循环,如遇到宏任务,则将其分发到宏任务队列中,若遇到微任务,则将其分发到微任务队列中。同步代码执行完毕,微任务队列先进入主线程直至任务执行完毕,下一个宏任务进入主线程开始下一次的事件循环。以上过程会不断重复,直到宏任务队列全部执行完毕,这就是Javascript中的事件循环机制了。

  • 44
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值