AWS Lambda中Promise.all调用S3 DeleteObjectCommand失效求助
问题根源
你的代码里Promise.all的用法存在错误——错误地将map返回的Promise数组额外包裹了一层数组,导致Promise.all并未真正等待所有删除操作完成。
看这段错误代码:
await Promise.all([ thumbSizes.map(async (size) => { return removeObject( destnBucketName, `resized-${size.width}X${size.height}_${fileName}` ); }), ]);
thumbSizes.map()本身会返回一个Promise<DeleteObjectCommandOutput>[]类型的数组,你把这个数组再放入[]中,就变成了[Promise<DeleteObjectCommandOutput>[]](一个包含数组的数组)。此时Promise.all会把内部数组当作一个已完成的Promise处理(因为数组本身不是Promise),代码会直接向下执行,而所有删除操作的Promise还在后台运行,Lambda函数可能在这些操作完成前就被环境终止。
至于PutObjectCommand看似正常,大概率是因为Put操作响应速度更快,在Lambda函数终止前刚好完成,但这是不可靠的偶然情况。本地环境中进程不会被强制终止,即使写法错误,后台的Promise最终也能执行完成,这就是本地与Lambda环境表现差异的核心原因。
解决方案
去掉多余的外层数组,直接将map返回的Promise数组传给Promise.all:
await Promise.all( thumbSizes.map((size) => { return removeObject( destnBucketName, `resized-${size.width}X${size.height}_${fileName}` ); }) );
另外,map里的async关键字可以移除,因为removeObject本身就返回Promise,不需要额外包装成async函数。
补充说明
Lambda函数的执行环境在函数返回结果后会被冻结或销毁,未完成的异步操作会被强制终止。而for...of循环中使用await是串行执行,每一步都等待操作完成后再进行下一个,因此能保证所有操作执行完毕,这也是替换成循环后功能正常的原因。
内容的提问来源于stack exchange,提问作者Vimson

