JS嵌套while循环时IndexedDB异步回调失效致无限循环
问题
我写了一段JS脚本,用来检查数据库里是否存在指定值:如果不存在就填充数据库;再次触发时从数据库读取值并展示在页面上。刷新页面执行两次脚本时功能正常,但把逻辑放进while循环后直接失效,循环无限运行。我猜是没法进入marmaInfo.onsuccess = function(evt) {}回调,不确定是不是作用域问题,搞不懂加了while循环后行为为啥差这么多。
相关代码:
var key = $('#marmaID').val(); console.log("key " + key ); store = getObjectStore("marmasStore", 'readwrite'); let i = 0; //while (i < 1) { var marmaInfo = store.get(key); marmaInfo.onsuccess = function(evt) { var req = store.openCursor(key); var record = evt.target.result; console.log("record:", record); req.onsuccess = function(e) { var cursor = e.target.result; if (cursor !== null) { // key already exist console.log(key + " FOUND"); console.log("load and display values of " + key); data = marmaInfo.result //build page // Marma information document.getElementById("headline").innerHTML = data.sanskrit; console.log("display values of " + key + " DONE"); i++; } else { // key not exist console.log(key + " NOT FOUND"); console.log("initial filling DB ..."); initialMarmaData.forEach((marma) => { var request = store.put(marma); request.onsuccess = (event) => { console.log(event.target.result + " initial filling DB DONE"); }; //return; }); //store.add(obj) } }; }; marmaInfo.onerror = function(evt) { console.log("error"); i++; } //}
原因与解决办法
核心问题:IndexedDB是异步操作,while是同步阻塞循环
IndexedDB的所有操作(比如store.get()、store.openCursor())都是异步非阻塞的,它们的回调函数(onsuccess/onerror)会在当前同步代码执行完之后,才会进入事件队列等待执行。
你加的while (i < 1)是同步阻塞循环,会一直占用主线程,导致所有IndexedDB的回调根本没机会执行——主线程被while死死占着,事件队列里的回调永远轮不到,自然i永远不会被修改,循环就无限跑下去了。
修正方案:用异步逻辑替代同步while循环
不需要用while循环,直接利用IndexedDB的异步回调逻辑来控制流程就行。如果确实需要重复执行直到满足条件,应该用递归调用或者Promise封装来实现异步循环,而不是同步while。
优化后的代码示例:
var key = $('#marmaID').val(); console.log("key " + key ); store = getObjectStore("marmasStore", 'readwrite'); // 封装成异步函数,避免同步阻塞 function checkAndProcessMarma() { var marmaInfo = store.get(key); marmaInfo.onsuccess = function(evt) { var req = store.openCursor(key); var record = evt.target.result; console.log("record:", record); req.onsuccess = function(e) { var cursor = e.target.result; if (cursor !== null) { // 已存在该key console.log(key + " FOUND"); console.log("加载并展示" + key + "的数据"); const data = marmaInfo.result; // 渲染页面 document.getElementById("headline").innerHTML = data.sanskrit; console.log("展示" + key + "的数据完成"); } else { // 不存在该key console.log(key + " NOT FOUND"); console.log("开始初始化填充数据库..."); // 用Promise.all等待所有put操作完成,确保数据写入后再进行后续逻辑 const putPromises = initialMarmaData.map(marma => { return new Promise((resolve, reject) => { var request = store.put(marma); request.onsuccess = (event) => { console.log(event.target.result + " 初始化写入完成"); resolve(); }; request.onerror = reject; }); }); // 所有数据写入后,重新检查并展示数据 Promise.all(putPromises).then(() => { console.log("全部初始化数据写入完成"); checkAndProcessMarma(); }).catch(err => { console.error("初始化写入失败:", err); }); } }; }; marmaInfo.onerror = function(evt) { console.log("查询出错"); } } // 启动流程 checkAndProcessMarma();
关键优化点:
- 把逻辑封装成异步函数,避免同步阻塞主线程
- 用
Promise.all等待所有数据库写入操作完成,确保数据落地后再重新查询 - 去掉了无意义的
i变量,改用异步回调的流程控制替代同步while循环
内容的提问来源于stack exchange,提问作者Corbinian
相关产品推荐
相关产品推荐

