JavaScript 中 await 永远不会 resolve 的 Promise 会导致内存泄露吗?

前言

在 JavaScript 中,await 关键字用于等待一个 Promise 完成,它只能在异步函数(async function)内部使用。当 await 一个永远不会 resolve 的 Promise 时,它确实会阻塞异步函数的进一步执行,但不会直接导致内存泄露(memory leak)。然而,这种情况可能会间接导致问题,特别是在处理资源(如数据库连接、文件句柄、网络请求等)时。

为什么说它不会直接导致内存泄露?

内存泄露通常指的是程序在不需要某些内存时未能释放它,导致内存使用量持续增加。在 JavaScript(特别是在 V8 引擎中,Chrome 和 Node.js 的 JavaScript 引擎)中,垃圾回收器(Garbage Collector, GC)会定期清理不再被引用的对象。如果一个 Promise 永远不会 resolve,那么它自身及其依赖的对象(除非有其他引用指向它们)最终会因为没有任何引用指向它们而被垃圾回收器回收。

间接问题

尽管 await 一个永远不会 resolve 的 Promise 不会直接导致内存泄露,但它可能导致以下问题:

  1. 阻塞执行:异步函数将停留在 await 表达式处,无法继续执行后续代码,这可能会阻塞事件循环中的其他任务。

  2. 资源占用:如果 Promise 依赖于某些外部资源(如数据库连接、文件句柄、网络请求等),这些资源将不会被释放,直到 Promise 被解决或拒绝。如果 Promise 永远不解决,这些资源可能会长时间被占用,甚至可能导致资源耗尽。

  3. 死锁和性能问题:在复杂的应用程序中,多个异步操作可能相互依赖。如果一个操作因为等待一个永远不会 resolve 的 Promise 而阻塞,它可能会阻止其他依赖它的操作执行,从而导致死锁或性能问题。

想要知道 promise 对象有没有被回收掉,可以在控制台使用 queryObjects() :

queryObjects(Promise) 做的是就是先手动执行一次垃圾回收,然后输出当前页面内存里还存在的 promise 对象。有 0 个,证明所有的 promise 对象都已经被回收了。

为了更明确的看到回收的确发生了,我们还可以给传入 test() 的 promise 对象和 test() 返回的 promise 对象都添加上垃圾回收的回调:

可以看到,这两万个永远不会 resolve 的 promise 都被回收了,这也是符合预期的。

JS 标准应该没有制定垃圾回收的具体细节,任何的对象何时被回收,甚至完全不回收,可能都不算是违反规范,毕竟 test262 里没有相关测试。不过规范实际制定时肯定还是要考虑逻辑上不能存在内存泄漏的。

所以这些都是引擎实现的知识,只有少数引擎开发能讲清楚这些细节,我只知道一点皮毛,下面是我的推测。

想要一个对象不被回收,必须有地方引用了它,除了直接引用,还可以间接的引用,比如:

new Promise((resolve, reject) => { 
  window.foo = resolve 
})

因为全局变量 foo 引用了 resolve 函数,这个函数比较特殊,在 C++ 层面其实引用了它所属的 promise 对象,所以会导致 promise 对象一直可达(reachable),也就无法被垃圾回收。

new Promise(resolve => {
  setTimeout(resolve, 10000)
})

像这个 promise,在 10 秒后才会被垃圾回收,10 秒内全局的任务队列里有个定时器任务引用了它,定时器执行完销毁后,这个 promise 对象就变成不可达的,从而也就被回收了。

如果 resolve 和 reject 都没被引用,它就会被直接回收掉:

new Promise(() => {})

除非有其它引用,比如你示例里的 p:

async function test(p) {
  await p
}

test(new Promise(() => {}))

这个局部变量 p 的确引用了 promise 对象,那这个 promise 被回收只有一个可能,就是 p 也不在了,实际上的确是,这个 test 函数的执行上下文也被回收了,虽然它还没执行完。

实际上 V8 的 async function 在 parser 阶段是被 desugar 成 generator 的 https://docs.google.com/document/d/1K38ct2dsxG_9OfmgErvFld4MPDC4Wkr8tPuqmSWu_3Y/edit,所以 test 函数在实际执行时可能类似于:

function* test(p) {
  yield p
  console.log(p)
}

test(new Promise(() => {}))

生成器在 yield p 这里停住,就类似于 await p 停住,因为已经没有办法引用到生成器的 next() 方法了,引擎就知道它不可能继续执行了,从而就一连串回收掉了所有的相关对象,具体的细节我是讲不清楚的。

解决方案

  • 超时机制:为 await 操作设置超时,以便在 Promise 无法在指定时间内 resolve 时采取适当的行动(如重试、记录错误或释放资源)。

  • 错误处理:确保 Promise 的错误处理逻辑是健全的,以便在 Promise 被拒绝时能够适当响应。

  • 资源清理:确保所有外部资源在使用完毕后都被正确释放,无论 Promise 是否 resolve。

  • 监控和日志:对异步操作进行监控,并在出现问题时记录详细的日志,以便快速定位和解决问题。

总之,虽然 await 一个永远不会 resolve 的 Promise 不会直接导致内存泄露,但它可能导致其他严重的问题,因此应该避免这种情况的发生。

仅供参考!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/798271.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构和算法(0-1)----递归

定义​ 递归是一种在程序设计中常用的技术,它允许一个函数调用自身来解决问题。递归通常用于解决那些可以被分解为相似的子问题的问题,这些问题的解决方式具有自相似性。在数据结构和算法中,递归是一种重要的解决问题的方法,尤其是…

从实时监控到风险智能预警:EasyCVR视频AI智能监控技术在工业制造中的应用

随着科技的不断进步和工业制造领域的持续发展,传统的生产管理方式正逐渐转型,迈向更加智能、高效和安全的新阶段。在这个变革过程中,视频智能监控技术凭借其独特的优势,成为工业制造领域的管理新引擎,推动着从“制造”…

前端直连小票打印机,前端静默打印,js静默打印解决方案

最近公司开发了一个vue3收银系统,需要使用小票打印机打印小票,但是又不想结账的时候弹出打印预览,找了很多方案,解决不了js打印弹出的打印预览窗口! 没办法,自己写了一个winform版本的静默打印软件&#xf…

我的智能辅助大师-办公小浣熊

一、基本介绍 随着2022年ChatGPT为代表的AI工具对互联网领域进行第一次冲击后,作为一名对编程领域涉足不算特别深的一名程序员,对AI大模型的接触也真的不能算少了,这是时代的必然趋势。在此之前也曾接触过很多的AI工具,他们都能在…

神经网络以及简单的神经网络模型实现

神经网络基本概念: 神经元(Neuron): 神经网络的基本单元,接收输入,应用权重并通过激活函数生成输出。 层(Layer): 神经网络由多层神经元组成。常见的层包括输入层、隐藏层…

React18+Redux+antd 项目实战 JS

React18Reduxantd 项目实战 js Ant Design插件官网 Axios官网 (可配置请求拦截器和响应拦截器) JavaScript官网 Echarts官网 一、项目前期准备 1.创建新项目 hotel-manager npx create-react-app hotel-manager2.安装依赖 //安装路由 npm i react-router-domnpm i aixos /…

manim学习笔记04:使用manim,表示向量和加法。

manim学习笔记04:使用manim,表示向量和加法。 一,相关定义 1.有向线段: 规定若线段 AB的端点为起点为A,B为终点,则线段就具有了从起点 A到终点 B的方向和长度。具有方向和长度的线段叫做有向线段。 接下…

练习9.5 彩票分析

练习 9.14:彩票 创建⼀个列表或元素,其中包含 10 个数和 5 个字 ⺟。从这个列表或元组中随机选择 4 个数或字⺟,并打印⼀条消息, 指出只要彩票上是这 4 个数或字⺟,就中⼤奖了。 练习 9.15:彩票分析 可以使…

【Qt 基础】绘图

画笔 QPen pen; pen.setWidth(3); // 线条宽度 pen.setColor(Qt::red);// 画笔颜色 pen.setStyle(Qt::DashLine);// 线条样式 pen.setCapStyle(Qt::RoundCap);// 线端样式 pen.setJoinStyle(Qt::BevelJoin);// 连接样式 painter.setPen(pen);线条 线端 连接 画刷 QBrush bru…

​前端Vue自定义签到获取积分弹框组件设计与实现

摘要 随着前端技术的不断演进,开发的复杂性日益凸显。传统的整体式开发方式在面临功能迭代和修改时,常常牵一发而动全身,导致开发效率低下和维护成本高昂。组件化开发作为一种解决方案,通过实现模块的独立开发和维护,…

【零基础】学JS之APIS第四天

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

(补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式

文章目录 前言一、进制1 逢几进一2 常见进制在java中的表示3 进制中的转换(1)任意进制转十进制(2)十进制转其他进制二、计算机中的存储1 计算机的存储规则(文本数据)(1)ASCII码表(2)编码规则的发展演化2 计算机的存储规则(图片数据)(1)分辨率、像素(2)黑白图与灰度…

澳门建筑插画:成都亚恒丰创教育科技有限公司

澳门建筑插画:绘就东方之珠的斑斓画卷 在浩瀚的中华大地上,澳门以其独特的地理位置和丰富的历史文化,如同一颗璀璨的明珠镶嵌在南国海疆。这座城市,不仅是东西方文化交融的典范,更是建筑艺术的宝库。当画笔轻触纸面&a…

装饰模式(大话设计模式)C/C++版本

装饰模式 需求分析: 1. 选择服饰 > 服饰类 2. 输出结果 对象是人 > 人类将Person类中一大堆服饰功能抽象出服饰类,然后通过Person类聚合服饰属性,通过Set行为来设置服饰属性,最后达到灵活打扮的效果 装饰模式 动态地给一个…

【Java--数据结构】栈:不仅仅是数据存储,它是编程的艺术

欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 栈 栈的方法介绍 入栈push 出栈pop和 瞄一眼peek 判空isEmpty和判满isFull 模拟实现栈 push入栈 pop出栈和peek 测试 使用泛型实现栈 测试 使用链表实现栈&#xff08…

【leetcode】整数反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0。 假设环境不允许存储 64 位整数(有符号或无符号)。 示例 1: …

运动控制问题

第一类运动控制问题是指被控制对象的空间位置或轨迹运动发生改变的运动控制系统的控制问题。这类运动控制问题在理论上完全遵循牛顿力学定律和运动学原则。 1、运动控制问题 第1类运动控制的核心是研究被控对象的运动轨迹 、分析运动路径、运动速度、加速度与时间的关系,常用…

【香菇带你学Linux】Linux环境下gcc编译安装【建议收藏】

文章目录 0. 前言1. 安装前准备工作1.1 创建weihu用户1.2 安装依赖包1.2.1 安装 GMP1.2.2 安装MPFR1.2.3 安装MPC 2. gcc10.0.1版本安装3. 报错解决3. 1. wget下载报错 4. 参考文档 0. 前言 gcc(GNU Compiler Collection)是GNU项目的一部分,…

TensorBoard ,PIL 和 OpenCV 在深度学习中的应用

重要工具介绍 TensorBoard: 是一个TensorFlow提供的强大工具,用于可视化和理解深度学习模型的训练过程和结果。下面我将介绍TensorBoard的相关知识和使用方法。 TensorBoard 简介 TensorBoard是TensorFlow提供的一个可视化工具,用于&#x…

JVM:垃圾回收器

文章目录 一、介绍二、年轻代-Serial垃圾回收器三、老年代-SerialOld垃圾回收器四、年轻代-ParNew垃圾回收器五、老年代-CMS(Concurrent Mark Sweep)垃圾回收器六、年轻代-Parllel Scavenge垃圾回收器七、Parallel Old垃圾回收器八、G1垃圾回收器 一、介…