事件环EvenLoop---浏览器篇

本文介绍了JavaScript中的事件环EvenLoop机制,详细讲解了同步与异步、任务队列、宏任务与微任务的概念,以及Promise和async/await的执行顺序。通过实例解析了EvenLoop的工作流程,帮助理解JavaScript的执行机制。
摘要由CSDN通过智能技术生成

事件环EvenLoop相信大家应该不陌生,但是涉及的方面还挺多的,要掌握起来还是有一定难度,希望这篇总结能帮到你们~

EvenLoop

我们先以一段代码作为示例

console.log('start');
setTimeout(() => {
  console.log('timeout');
});
Promise.resolve().then(() => {
  console.log('resolve');
});
console.log('end');

按照js将代码按顺序压入执行栈来说,结果应该为:

start 
timeout 
resolve 
end

不不不!既然今天我们要讲的是EvenLoop事件环,我们就知道没这么简单了,我们看看最后执行的结果为:

start
end
resolve
timeout

不要慌,接下来让我们一起探个究竟:

  • 刚开始整个脚本作为一个宏任务来执行,对于同步代码直接压入执行栈 中

等等…宏任务?同步? 感到陌生没关系,我们先放下这道题,待会再回头分许。接下来我们一起学习一些概念

同步与异步

为什么要有异步?

我们知道js是单线程的,这意味着任务是一个接一个进行,可能遇到执行时间长的就会阻塞程序,效率太慢;这时就产生了异步任务,提高效率

概念
  • 同步:一旦发出调用,就主动等待返回的结果,即同步任务是一个接一个执行
  • 异步:调用者发出调用后,不会等待结果,而是将异步回调函数加入到任务队列中等待被执行

异步一般是指 网络请求、计时器、DOM事件监听

任务队列

什么是任务队列呢?

  • js所有同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外还有一个任务队列。一旦执行栈中所有同步任务执行完毕,就会读取任务队列中的事件
  • 主线程不断重复以上步骤

宏任务与微任务

在js中所有任务分为宏任务和微任务

  • 宏任务:主代码块、定时器(setTimeout、setInterval)
  • 微任务:Promise、process.nextTick等

每次执行栈的代码就是一个宏任务,包括任务队列中的任务,每执行宏任务之前浏览器都会重新渲染页面。

微任务

为什么要有微任务?什么时候执行?
解决异步回调问题

  • 当产生了异步函数时,需要放入宏任务队列。此时需要等到前面所有宏任务执行完后,才能执行该回调,如果任务队列非诚长,就会造成应用卡顿
  • 为了解决这个问题,在每一个宏任务中定义了一个微任务队列,执行完当前宏任务后,就会检查当前微任务队列,如果为空就执行下一个宏任务,否则先执行微任务

类似银行业务办理,每个人排队等待办理自己的业务就是一个宏任务。当我办理好银行卡后,想临时增加一个 办理套餐业务(微任务),此时不会重新叫号再排一次队,而是继续办理完成后,才轮到下一个(宏任务)

总结一下:

  • 微任务在当前宏任务执行完后执行,浏览器重新渲染页面之前。
  • 微任务:promise、process.nextNick
  • 一般考察setTimeout和promise,此时promise优先执行

回到EvenLoop

有了前面知识的铺垫,我们对js执行有了一定了解,现在让我们总结一下

  1. JS引擎维护一个执行栈,同步代码会一次加入到执行栈中依次执行并出栈
  2. 遇到异步函数,将异步交给对应WebApi,继续执行后面的任务
  3. WebApi在条件满足时(如 触发点击事件),将对应的回调加入到任务队列中
  4. 执行栈为空时(即所有同步处理完成后),js引擎会去任务队列中取事件,加入到执行栈中执行
  5. 处理完成后出栈,重复上述操作,这就是事件循环EvenLoop机制

执行完宏任务时,要检查微任务队列,有则先执行,接着再执行浏览器UI线程的渲染工作

EvenLoop示意图

练习

我们先回到最开始的题目

console.log('start');
setTimeout(() => {
  console.log('timeout');
});
Promise.resolve().then(() => {
  console.log('resolve');
});
console.log('end');

相信不难分析:

  1. 先执行当前主线程的宏任务,即所有同步代码 打印start、end
  2. setTimeout放入宏任务队列
  3. Promise.then放入微任务队列
  4. 执行完宏任务,检查发现Promise.then,执行 打印 resolve
  5. 执行下一个宏任务 setTimeout 打印timeout

结果就很容易分析出来啦,不过这里要注意Promise本身属于同步代码,其promise.then才是异步操作

Promise

Promoise是为了解决回调地狱而产生,使用then方法链式编程。Promise意思是‘承诺’,可以当作一个容器,包含未来产生的结果,其结果有不同状态

Promise立即执行

新建一个Promose函数立即执行,因为Promise本身是同步的,其内部的then指定的回调函数才是异步,具体可以参考以下链接学习:

ES6中Promise学习

async/await

async表示该函数也是Promise异步函数,其await之前的代码也是立即执行,await后面的代码会执行,然后将结果放入任务队列中等待

  • 再观察以下例子:
console.log('script start');

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

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

应该不难得出结果如下:

script start
promise1
script end
promise2
setTimeout

关于浏览器事件环讲解到此结束,如有错漏,欢迎指正~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值