IDBOpenDBRequest取消方法及相关代码必要性咨询
IndexedDB 打开请求异常处理问题
我有如下代码片段:
function open_db(dbname, dbversion, upgrade, onblocked) { if (upgrade === undefined) { upgrade = function basic_init(ev) { … }; } if (onblocked === undefined) { onblocked = function onblocked(ev) { throw ev; }; } let req = window.indexedDB.open(dbname, dbversion); return new Promise((resolve, reject) => { req.onsuccess = ev => resolve(ev.target.result); req.onerror = ev => reject(ev.target.error); req.onupgradeneeded = ev => { try { return upgrade(ev); } catch (error) { reject(error); ev.target.onsuccess = ev => ev.target.close(); // 这行有必要吗? throw error; // 这行是多余的吗? } }; req.onblocked = ev => { try { return onblocked(ev); } catch (error) { reject(error); ev.target.onsuccess = ev => ev.target.close(); // 这行有必要吗? throw error; // 这行是多余的吗? } }; }); }
如果.onblocked或.onupgradeneeded处理函数抛出原生错误,是否会取消本次数据库打开尝试?还是IDBOpenDBRequest对象会忽略这类错误继续执行,直到数据库成功打开后需要手动关闭连接?
简而言之:代码中被注释标注的两行代码是否必要?这两行代码是否足以避免产生悬空的数据库打开句柄?
除了添加.onsuccess = ev => … .close()的处理逻辑外,是否存在更优的取消数据库打开请求的实现方式?
结论
- 先明确IndexedDB的核心行为:
onupgradeneeded/onblocked回调中抛出的错误不会自动终止数据库打开流程。- 针对
onupgradeneeded场景:回调抛错后请求确实会进入错误状态触发onerror,但存在时序漏洞——极端情况下成功事件可能先于错误事件触发,返回已建立的数据库连接,这个连接不会自动关闭,会形成悬空句柄。你写的ev.target.onsuccess = ev => ev.target.close()是有必要的,正好能兜底这个场景。 - 针对
onblocked场景:这个回调触发时,请求正处于挂起状态(等待其他标签页关闭旧版本连接),此时抛错浏览器会直接忽略,请求既不会失败也不会取消,会一直等到其他连接释放后继续执行打开流程,如果没有兜底逻辑,后续会意外建立无人管理的悬空连接。
- 针对
- 代码中两处
throw error是完全多余的。这两个回调属于浏览器事件队列的独立执行上下文,抛出的错误不会被外层Promise捕获,只会触发全局未捕获错误,你已经手动调用了reject(error),不需要再额外抛错。 - 你写的关闭逻辑基本可以覆盖大部分悬空句柄场景,但还有更稳妥的取消实现,不需要依赖覆盖
onsuccess的时序:- 在
onupgradeneeded捕获到错误后,第一时间调用ev.target.transaction?.abort()终止正在运行的版本变更事务,可以主动让打开请求快速进入失败状态,减少不确定的等待时间。 - 不要在事件回调内抛错,所有异常都通过Promise的reject通道传递,避免污染全局错误。
onblocked场景如果确定要取消打开,可以额外加超时兜底,避免请求永久挂起。
- 在
优化后的异常处理片段参考:
req.onupgradeneeded = ev => { try { upgrade(ev); } catch (error) { reject(error); // 主动终止升级事务,加速请求失败 ev.target.transaction?.abort(); // 兜底关闭时序漏洞下可能成功建立的连接 req.onsuccess = e => e.target.result.close(); } }; req.onblocked = ev => { try { onblocked(ev); } catch (error) { reject(error); // 兜底关闭阻塞解除后可能成功建立的连接 req.onsuccess = e => e.target.result.close(); } };
内容的提问来源于stack exchange,提问作者JamesTheAwesomeDude
相关产品推荐
相关产品推荐

