面试高频的事件循环

事件循环(Event Loop)

JavaScript是一门单线程的语言,在同一时间只能做一件事,js里面的代码要按照顺序逐行执行代码?比如说我们浏览新闻想要获取新闻图片,如果网络卡顿,获取了很长时间,难道就这样干等着吗?那肯定是不会的,给到用户的体验也是不好的

在js里所有的任务可以被分为同步任务异步任务 ,异步任务里又有微任务宏任务,

当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务

事件循环运行机制

这种事件循环机制是由 JavaScript 的宿主环境来实现的,在浏览器运行环境中由浏览器内核引擎实现,而在 NodeJS 中则由 libuv 引擎实现。

主线程运行时候,产生堆(Heap)和栈(Stack),栈中的代码调用各种外部 API,它们在任务队列中加入各种事件。只要栈中的代码执行完毕,主线程就会通过事件循环机制读取任务队列,依次执行那些事件所对应的回调函数。

运行机制:

  1. 所有同步任务都在主线程上执行,形成一个 执行栈(Execution Context Stack)
  2. 主线程之外,还存在一个 任务队列 (Task Queue)。只要异步任务有了运行结果,就在 任务队列 之中放置一个事件
  3. 一旦 执行栈 中的所有同步任务执行完毕,系统就会读取 任务队列,看看里面有哪些待执行事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
  4. 主线程不断重复上面的第三步

浏览器环境

JavaScript 的异步任务根据事件分类分为两种:宏任务(MacroTask)和微任务(MicroTask

微任务:一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前

常见的微任务有:

markdown 复制代码
*   Promise.then
*   MutaionObserver
*   Object.observe(已废弃;Proxy 对象替代)
*   process.nextTick(Node.js)

宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合

常见的宏任务有:

markdown 复制代码
*   setTimeout/setInterval

*   UI rendering/UI事件

*   postMessage、MessageChannel

*   setImmediate、I/O(Node.js)

微任务与宏任务的区别

宏任务与微任务的区别在于队列中事件的执行优先级。进入整体代码(宏任务)后,开始首次事件循环,当执行上下文栈清空后,事件循环机制会优先检测微任务队列中的事件并推至主线程执行,当微任务队列清空后,才会去检测宏任务队列中的事件,再将事件推至主线程中执行,而当执行上下文栈再次清空后,事件循环机制又会检测微任务队列,如此反复循环。

宏任务与微任务的优先级

  • 宏任务的优先级高于微任务
  • 每个宏任务执行完毕后都必须将当前的微任务队列清空
  • 第一个 <script> 标签的代码是第一个宏任务
  • process.nextTick 优先级高于 Promise.then

示例代码

js 复制代码
setTimeout(function() {
    console.log('1');
})

new Promise(function(resolve) {
    console.log('2');
}).then(function() {
    console.log('3');
})

console.log('4'); // 2 4 3 1
  • 这段代码作为宏任务,进入主线程。
  • 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)
  • 接下来遇到了Promisenew Promise立即执行,then函数分发到微任务Event Queue。
  • 遇到console.log(),立即执行。
  • 好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
  • ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event QueuesetTimeout对应的回调函数,立即执行。
  • 结束。

async与awite

async关键字是asynchronous(异步)的简写,用来声明一个函数是异步函数,写在函数的最前面,他会返回一个promise对象

awite可以理解为asynchronous waite(等待异步),他会等待一个异步任务返回的结果

下面这两种方法是等效的

js 复制代码
function fn() {
    return Promise.resolve('TEST');
}

async function asyncFn() {
    return 'TEST';
}

awite

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值

js 复制代码
async function fn(){
    // 等同于
    // return 123
    return await 123
}
fn().then(a => console.log(a)) // 123

不管await后面跟着的是什么,await都会阻塞后面的代码

js 复制代码
async function fn1 (){
    console.log(1)
    await fn2()
    console.log(2) // 阻塞
}

async function fn2 (){
    console.log('fn2')
}

fn1()
console.log(3)
//1 fn2 3 2 

上面的例子中,await 会阻塞下面的代码(即加入微任务队列),先执行 async 外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax