Vanilla JS下IndexedDB未同步数据与服务器同步的异步逻辑实现求助
问题描述
我用Vanilla JavaScript开发,用IndexedDB存储名为punches的事件记录,记录结构示例:
{punchedon: 1687522698067, controlpointid: '1', method: '3', qrcodetext: '4', latitude: '5', ... synchronized: 0}
每条记录包含synchronized字段(值为1/0),标记是否已与服务器同步。
需要实现的逻辑:浏览器恢复联网时,遍历所有synchronized=0的未同步记录,发送至服务器,仅当服务器响应成功时,将该记录的synchronized字段更新为1。
但现有伪代码存在逻辑问题——即使服务器请求失败,仍会将synchronized设为1,伪代码如下:
function syncPunch(punch) { httpRequest = new XMLHttpRequest(); if (!httpRequest) { return false; } httpRequest.onreadystatechange = alertContents; httpRequest.open("GET", "https://server/punch.php?controlid=123&teamid=123×tamp=ABC...."); // fake call httpRequest.send(); } function syncUnsynced() { // sync all not yet synced punches let tx = db.transaction(['punches'], 'readwrite'); let store = tx.objectStore('punches'); let syncIndex = store.index('synchronized'); // I have an index on the synchronized field, I query only those synchronized=0 let keyRng = IDBKeyRange.only(0); const cursorRequest = syncIndex.openCursor(keyRng); cursorRequest.onsuccess = e => { const cursor = e.target.result; if (cursor) { const punch = cursor.value; const punchKey = cursor.value.key; synchPunch(punch); // here I need to wait (react on / callback / promise??) to make sure the call was succesfull console.log("Now fake-syncying " + JSON.stringify(punch)); punch.synchronized = 1; const updateRequest = cursor.update(punch); // if this update fails and the call was sucesfull, I can live with that. But in normal scenario, I don't want to sync something that has been synced already. console.log("Updated " + punchKey); cursor.continue(); } } }
我尝试理解Promise相关文档但未能掌握,恳请帮助实现正确的同步逻辑。
解决方案
1. 把syncPunch改造为返回Promise的函数
将异步HTTP请求封装成Promise,这样就能明确等待请求完成,区分成功/失败状态:
function syncPunch(punch) { return new Promise((resolve, reject) => { const httpRequest = new XMLHttpRequest(); if (!httpRequest) { reject(new Error("无法创建XMLHttpRequest对象")); return; } httpRequest.onreadystatechange = function() { if (httpRequest.readyState === XMLHttpRequest.DONE) { // 可根据实际业务调整成功判断条件,比如状态码200+服务器返回成功标识 if (httpRequest.status === 200) { const response = httpRequest.responseText; // 假设服务器返回包含"success"表示记录同步成功 if (response.includes("success")) { resolve(true); } else { reject(new Error("服务器返回失败响应")); } } else { reject(new Error(`请求失败,状态码:${httpRequest.status}`)); } } }; // 用punch的真实数据拼接请求URL const url = `https://server/punch.php?controlid=${punch.controlpointid}&teamid=123×tamp=${punch.punchedon}`; httpRequest.open("GET", url); httpRequest.send(); }); }
2. 改造syncUnsynced函数,处理异步流程
利用async/await配合IndexedDB游标,确保前一条记录的同步请求完成后再处理下一条:
async function syncUnsynced() { const tx = db.transaction(['punches'], 'readwrite'); const store = tx.objectStore('punches'); const syncIndex = store.index('synchronized'); const keyRng = IDBKeyRange.only(0); const cursorRequest = syncIndex.openCursor(keyRng); cursorRequest.onsuccess = async (e) => { const cursor = e.target.result; if (cursor) { const punch = cursor.value; const punchKey = cursor.key; // 注意:用cursor.key获取记录主键,而非cursor.value.key try { console.log("正在同步记录:" + JSON.stringify(punch)); // 等待同步请求完成 await syncPunch(punch); // 同步成功,更新synchronized字段 punch.synchronized = 1; // 等待IndexedDB更新操作完成 await new Promise((resolve, reject) => { const updateRequest = cursor.update(punch); updateRequest.onsuccess = () => resolve(); updateRequest.onerror = () => reject(updateRequest.error); }); console.log("记录同步并更新成功:" + punchKey); } catch (error) { console.error("记录同步失败:", error); // 同步失败,不更新字段,留待下次重试 } // 处理下一条记录 cursor.continue(); } else { console.log("所有未同步记录处理完成"); } }; cursorRequest.onerror = (e) => { console.error("获取未同步记录失败:", e.target.error); }; }
3. 监听联网状态恢复事件
添加浏览器在线状态监听,自动触发同步:
window.addEventListener('online', () => { console.log("网络已恢复,开始同步未上传记录"); syncUnsynced(); });
关键说明
- Promise封装HTTP请求:将异步请求转为Promise,让我们能精准等待请求结束,区分成功/失败场景。
- 异步游标处理:用
async/await控制游标流程,避免并发请求过多导致的服务器压力或数据混乱。 - 错误容错:通过
try/catch捕获同步过程中的错误,失败时不修改synchronized字段,保留记录以便后续重试。
内容的提问来源于stack exchange,提问作者Adam Muller
相关产品推荐
相关产品推荐

