Node.js/TypeScript中使用AWS-SDK处理UnhandledPromiseRejectionWarning报错
Hey,我帮你排查了下这个UnhandledPromiseRejectionWarning的问题,主要是两个核心原因导致的,咱们一步步拆解:
1. 混合async/await与.then/.catch的写法陷阱
你在异步函数里同时混用了await和Promise链式调用(.then/.catch),这种写法很容易让错误处理逻辑出现“漏网之鱼”。比如在deleteVersions里,你用await包裹了整个Promise链,但.then内部的异步操作(await this.deleteObjects(...))如果抛出错误,虽然你在.catch里重新抛出了,但这种混合写法会打乱Promise的状态处理逻辑,有时候错误会意外绕过外层的try/catch。
2. 未处理空对象数组的场景
当存储桶已经没有版本、删除标记或者剩余对象时,data.Versions、data.DeleteMarkers或者data.Contents会是空数组甚至undefined。这时候你直接传给deleteObjects,生成的Delete.Objects会是空数组,而AWS S3的deleteObjects接口根本不接受空数组,会直接报错。你的代码里没做这个判断,导致错误被抛出后,虽然有catch但又重新throw,如果调用emptyS3Bucket的地方没处理这个错误,就会触发未处理的Promise拒绝警告。
修复后的代码示例
我把你的代码重构了下,统一用async/await写法,同时补上了空数组的判断逻辑,错误处理会更稳妥:
async emptyS3Bucket(bucketName: string) { try { await this.deleteVersions(bucketName); await this.deleteMarkers(bucketName); await this.deleteContents(bucketName); } catch (error) { // 可根据需求做自定义错误处理,比如打日志、转换错误格式 console.error('清空S3存储桶失败:', error); throw error; // 若需要上层处理则保留,否则可移除 } } private async deleteVersions(bucketName: string) { const data = await this.s3.listObjectVersions({ Bucket: bucketName }).promise(); // 先判断是否有版本需要删除 if (data.Versions?.length) { await this.deleteObjects(data.Versions, bucketName); } } private async deleteMarkers(bucketName: string) { const data = await this.s3.listObjectVersions({ Bucket: bucketName }).promise(); if (data.DeleteMarkers?.length) { await this.deleteObjects(data.DeleteMarkers, bucketName); } } private async deleteContents(bucketName: string) { const data = await this.s3.listObjectsV2({ Bucket: bucketName }).promise(); if (data.Contents?.length) { await this.deleteObjects(data.Contents, bucketName); } } private async deleteObjects(objects: any[], bucketName: string) { // 提前过滤空数组,避免调用S3接口报错 if (!objects?.length) return; const deleteParams = { Bucket: bucketName, Delete: { Objects: objects.map(({ Key, VersionId }) => ({ Key, VersionId })) } }; try { const result = await this.s3.deleteObjects(deleteParams).promise(); // 可处理删除成功的逻辑,比如记录删除的对象列表 console.log('成功删除对象:', result.Deleted); } catch (error) { console.error('删除对象出错:', error); throw error; // 向上层传递错误,让调用方统一处理 } }
关键修复点说明
- 统一
async/await写法:去掉所有.then/.catch,改用try/catch处理错误,让异步逻辑更直观线性,彻底避免Promise链的状态混乱问题。 - 空数组前置判断:在每个删除步骤和
deleteObjects里都先检查要处理的对象是否存在且非空,避免调用S3的deleteObjects接口时传入空数组导致报错。 - 错误处理优化:在catch块里增加了日志打印,方便排查问题,同时可灵活选择是否向上层抛出错误(适配不同的错误处理策略)。
额外提醒
最后要注意:如果调用emptyS3Bucket的地方没有用try/catch或者.catch()处理错误,哪怕emptyS3Bucket里有catch并重新抛出,这个错误还是会变成未处理的Promise拒绝。所以调用的时候一定要加错误处理:
// 调用示例 try { await yourService.emptyS3Bucket('your-bucket-name'); } catch (err) { console.error('处理清空存储桶错误:', err); }
另外,如果你的存储桶里有大量对象,listObjectVersions和listObjectsV2会返回分页结果,你还需要处理IsTruncated和NextContinuationToken来遍历所有对象,不然只会删除第一页的内容哦。
内容的提问来源于stack exchange,提问作者GThree

