浅谈js的事件循环(Event Loop)

本文结合文章与开发经验,探讨JavaScript事件循环机制。介绍了js是单线程语言及原因,还阐述同步任务、异步任务、任务队列等概念,强调宏任务与微任务关系,指出了解该机制有助于解决异步问题,也是前端面试常考点。
  • 事件循环是js这门语言的一大特点。
  • 了解事件循环机制,有助于日常开发中遇到的一些异步问题。
  • 而且还是前端面试一经常考点。
  • 故本人结合一些文章和个人的一些开发经验,浅淡一下

一,js是一门单线程语言

  1. js的单线程
a. js是一门单线程的语言。这意味着它在同一时间,只能做同一件事。
b. 但为了协调事件,用户交互,UI渲染和网络行为交互等。
c. 防止主线程被阻塞,Event Loop便应运而生。
如: 发送一个网络请求,需要等待一定时间,这个时间内主线程空闲出来做些其他事;
复制代码
  1. 为什么js是单线程?
a. js主要是运行在浏览器的脚步语言,主要是操作dom;
b. 举个例子,如果js同时有多个线程。多个线程同时操作同一个dom,
   这时浏览器该依据那个线程,如何判断优先级
c. 为了避免上述问题,并降低复杂度,故js被设计成单线程语言。
复制代码

二,概念的理解

  1. 同步任务
同步任务指的是,在主线程上排队执行的任务,
只有前一个任务执行完毕,才能执行后一个任务;
复制代码
  1. 异步任务
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,
只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
复制代码
  1. 异步执行机制
a. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack);
b. 主线程之外,还存在一个"任务队列"(task queue)。
   只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
c. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",
   看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,
   进入执行栈,开始执行。
d. 主线程不断重复上面的第三步。
复制代码
  1. 任务队列
"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。
复制代码
  1. 事件循环
主线程从"任务队列"中读取事件,这个过程是循环不断的,
所以整个的这种运行机制又称为Event Loop(事件循环)。
复制代码
  1. 宏任务与微任务
异步任务分为 宏任务(macrotask) 与 微任务 (microtask),
不同的API注册的任务会依次进入自身对应的队列中,
然后等待 Event Loop 将它们依次压入执行栈中执行。

宏任务:script(整体代码)、setTimeout、setInterval、UI 渲染、 
        I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
        
微任务:Promise、 MutaionObserver、process.nextTick(Node.js环境)
复制代码
  1. Event Loop(事件循环)
1)执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行;
(2)检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列;
(3)更新render(每一次事件循环,浏览器都可能会去更新渲染);
(4)重复以上步骤;
复制代码
  1. 宏任务 > 所有微任务(核心),上代码
<script>// 宏任务1
    console.log('宏任务1'); // 宏任务1中的同步任务
    
    setTimeout(() => {// 宏任务1中的另一个宏任务3
        console.log('宏任务1中的另一个宏任务3');
        
        new Promise((resolve, reject) => {
            resolve('宏任务3中的微任务2');
        }).then(data => {// 宏任务3中的微任务2
            console.log(data)
        })
        
    }, 300);
    
    new Promise((resolve, reject) => {
        resolve('宏任务1中的微任务1');
    }).then(data => {// 宏任务1中的微任务1
        console.log(data);
        
        setTimeout(() => {// 微任务1中的另一个宏任务4
            console.log('微任务1中的另一个宏任务4');
        }, 300);
        
    });
    
</script>// 宏任务1

<script>// 宏任务2
    console.log('宏任务2')
</script>// 宏任务2
复制代码
  • 上述代码的执行结果

  • 代码结果分析:
1. 宏任务1=>宏任务1中的微任务1
   表明执行完宏任务就执行微任务(忽略宏任务2,便于理解)
2. 然后到 宏任务1中宏任务3=>宏任务3中的微任务2
   再次表明执行完本宏任务后就执行本宏任务下的微任务
3. 最后到微任务1中的宏任务4
复制代码

转载于:https://juejin.im/post/5cd066066fb9a03202222eac

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值