Promise.allSettled未等待Promise完成,如何解决?
问题
我有一个通过AJAX向服务器发送各类更新的函数,需要在所有更新发送完成后通知用户。我创建了Promise数组并使用Promise.allSettled,但该方法在多数Promise仍处于pending状态时就继续执行。
代码中通过IndexedDB打开pending-updates数据库,读取item-updates对象存储中的记录,将postItemUpdates返回的Promise加入promises数组。但控制台显示“all updates sent”日志早于“Sending 2 Item Updates To Server”,使用await delay(500)可临时解决但并非理想方案。控制台中then函数的values为空,成功消息直接触发,请问为何then函数会在Promise完成前触发?
相关代码片段
$('#updateModal').modal('show'); console.log('Sending Updates To Server'); let DBOpenRequest = window.indexedDB.open('pending-updates'); var promises=[]; var recordsToDelete=[]; DBOpenRequest.onsuccess = function(event) { var db = event.target.result; // db transaction var itemUpdatePromises = []; itemUpdatesTransaction = db.transaction('item-updates', 'readwrite'), // get object store itemUpdates = itemUpdatesTransaction.objectStore('item-updates'), // get all records in object store itemUpdatesRecords = itemUpdates.getAll(); itemUpdatesRecords.onsuccess = () => { if (typeof itemUpdatesRecords.result!=='undefined') { if (itemUpdatesRecords.result.length>0) { recordsToDelete['itemUpdates']=itemUpdatesRecords.result; console.log('Sending '+itemUpdatesRecords.result.length+' Item Updates To Server'); addElement('logWindow', '<p>Sending '+itemUpdatesRecords.result.length+' Item Updates To Server <i id="itemUpdateIcon" class="fa-duotone fa-paper-plane"></i></p>') $.each(itemUpdatesRecords.result, function( index, value ) { var promise = postItemUpdates(value); promises.push(promise); }); } } }; itemUpdatesRecords.onerror = () => { //db.close; console.log('Item Updates Object Store Not Found', itemUpdatesRecords.error); };
await delay(500); // this allowed the time for the promises array to populate before continuing - not an ideal solution but solved my issue. console.log("before", promises.join(",")) Promise.allSettled(promises).then(function (values) { console.log("after", values.join(",")); const rejected = values.filter(result => result.status === 'rejected').map(result => result.reason); if ((Array.isArray(rejected) && rejected.length > 0) || typeof rejected === 'undefined') { errorMsg('There Was A Problem Sending Updates To Server'); } console.log('all updates sent')
解答
核心问题是IndexedDB的操作是异步回调模式,你执行Promise.allSettled时,IndexedDB还没完成数据读取,promises数组还是空的。
具体原因
DBOpenRequest.onsuccess和itemUpdatesRecords.onsuccess都是异步回调,这些回调会在当前同步代码全部执行完之后才触发。- 你写的
await delay(500)之后的代码(包括Promise.allSettled)会先于IndexedDB的回调执行,此时promises数组还没被填充,Promise.allSettled([])会立即resolve,直接进入then函数打印“all updates sent”。 - 之后IndexedDB的回调才触发,往
promises数组里加Promise,但此时Promise.allSettled已经执行完毕,根本不会等待这些新加的Promise。
正确解决方法
把整个IndexedDB的操作包装成Promise,确保在数据读取完成、promises数组填充完毕后,再执行Promise.allSettled。
修改后的代码示例:
$('#updateModal').modal('show'); console.log('Sending Updates To Server'); // 将IndexedDB操作封装为Promise const getPendingUpdates = () => { return new Promise((resolve, reject) => { const DBOpenRequest = window.indexedDB.open('pending-updates'); const promises = []; const recordsToDelete = []; DBOpenRequest.onsuccess = (event) => { const db = event.target.result; const itemUpdatesTransaction = db.transaction('item-updates', 'readwrite'); const itemUpdates = itemUpdatesTransaction.objectStore('item-updates'); const itemUpdatesRecords = itemUpdates.getAll(); itemUpdatesRecords.onsuccess = () => { if (itemUpdatesRecords.result?.length > 0) { recordsToDelete['itemUpdates'] = itemUpdatesRecords.result; console.log(`Sending ${itemUpdatesRecords.result.length} Item Updates To Server`); addElement('logWindow', `<p>Sending ${itemUpdatesRecords.result.length} Item Updates To Server <i id="itemUpdateIcon" class="fa-duotone fa-paper-plane"></i></p>`); // 填充Promise数组 itemUpdatesRecords.result.forEach(value => { promises.push(postItemUpdates(value)); }); } // 数据读取完成,返回Promise数组和待删除记录 resolve({ promises, recordsToDelete }); }; itemUpdatesRecords.onerror = () => { console.log('Item Updates Object Store Not Found', itemUpdatesRecords.error); reject(itemUpdatesRecords.error); }; }; DBOpenRequest.onerror = () => { reject(DBOpenRequest.error); }; }); }; // 按顺序执行异步流程 (async () => { try { const { promises } = await getPendingUpdates(); console.log("before", promises.join(",")); const values = await Promise.allSettled(promises); console.log("after", values.join(",")); const rejected = values.filter(result => result.status === 'rejected').map(result => result.reason); if (rejected.length > 0) { errorMsg('There Was A Problem Sending Updates To Server'); } console.log('all updates sent'); } catch (err) { console.error('Failed to load pending updates:', err); errorMsg('There Was A Problem Loading Pending Updates'); } })();
关键改动点
- 将IndexedDB的异步回调操作全部包裹在一个Promise中,只有当数据读取完成、promises数组填充好后才resolve。
- 使用
async/await等待IndexedDB操作完成,再执行Promise.allSettled,确保数组已经有内容。 - 移除了不可靠的
delay(500),完全基于异步操作的实际完成时机控制流程。
内容的提问来源于stack exchange,提问作者AKAust
相关产品推荐
相关产品推荐

