为何await Promise.all未阻塞执行?求原因与解决方法
问题:await Promise.all未等待,后续代码提前执行
你遇到的情况是:代码里明明在Promise.all前加了await,但后面处理NDVI统计的代码总是先执行,完全没等Promise.all里的文件写入操作完成。
原因分析
核心问题出在Promise.all接收的数组上:
- 你在
bandsDataForTS1.map和bandsDataForTS2.map的回调里,调用了IOUtils.writeForPath(异步操作,返回Promise),但没有把这个Promise返回给map。 - map函数最终生成的数组里全是
undefined,而非Promise实例。Promise.all遇到非Promise元素会直接当作已完成状态,所以它会立刻“结束”,直接进入后续的统计逻辑,根本不会等文件写入完成。
另外还有个小问题:.catch里引用的ioResults在错误场景下不存在,会触发额外报错。
解决办法
步骤1:让map返回Promise
把每个map回调里的IOUtils.writeForPath整个Promise链返回出去,这样Promise.all才能拿到真正的Promise数组,等待所有异步操作完成。
步骤2:(可选)改用async/await简化代码
把.then/.catch换成async/await写法,代码更易读,错误处理也更直观。
修改后的完整代码示例:
.then(async (resp) => { bandsDataForTS1 = resp[0]; bandsDataForTS2 = resp[1]; await Promise.all([ ...bandsDataForTS1.map(async (data, index) => { try { const ioResults = await IOUtils.writeForPath( titlesOfBandsAsGeoTIFFForTS1[index], IOResources.CONST_OUTPUT_PATH_FOR_TIFFS.description, data.contents, '.tiff', ); processAPIBandsAsGeoTIFFFileNamesForTS1.push(ioResults.fileName); processAPIBandsAsGeoTIFFFullPathsForTS1.push(ioResults.fullPath); this.#msg = ({ msg: infoTag + 'WS:/*************************************++WSSentinelhubProcessAPINDVIsProcessor/ tiff-contents are written to file successfully.', processAPIPathOfBandsDataAsGeoTIFFFForTS1: ioResults.fullPath, processAPIFileNameOFBandsDataAsGeoTIFFForTS1: ioResults.fileName, }); console.info(this.#msg); } catch (e) { this.#msg = ({ msg: infoTag + 'WS:/WSSentinelhubProcessAPINDVIsProcessor/ tiff-contents failed to be written to file.', processAPITargetFileName: titlesOfBandsAsGeoTIFFForTS1[index] }); console.info(this.#msg); throw new Error(this.#msg); } }), ...bandsDataForTS2.map(async (data, index) => { try { const ioResults = await IOUtils.writeForPath( titlesOfBandsAsGeoTIFFForTS2[index], IOResources.CONST_OUTPUT_PATH_FOR_TIFFS.description, data.contents, '.tiff', ); processAPIBandsAsGeoTIFFFileNamesForTS2.push(ioResults.fileName); processAPIBandsAsGeoTIFFFullPathsForTS2.push(ioResults.fullPath); this.#msg = ({ msg: infoTag + 'WS:/WSSentinelhubProcessAPINDVIsProcessor/ tiff-contents are written to file successfully.', processAPIPathOfBandsDataAsGeoTIFFFForTS2: ioResults.fullPath, processAPIFileNameOFBandsDataAsGeoTIFFForTS2: ioResults.fileName, }); console.info(this.#msg); } catch (e) { this.#msg = ({ msg: infoTag + 'WS:/WSSentinelhubProcessAPINDVIsProcessor/ tiff-contents failed to be written to file.', processAPITargetFileName: titlesOfBandsAsGeoTIFFForTS2[index] }); console.info(this.#msg); throw new Error(this.#msg); } }), ]); // 直接在这里写统计逻辑,无需额外嵌套.then let averagesNDVIs = null; let sorted = null; let minMaxNDVI = null; // 注:索引[0]的元素是我们需要的NDVI平均值,波段顺序为:[平均值, 无云样本数, 有云样本数, 无云占比, 有云占比] averagesNDVIs = bandsDataForTS1[0].contents; sorted = averagesNDVIs.sort((a, b) => a-b); minMaxNDVI = MathUtils.findMinMax('_NDVI_averages_GeoTIFF_TS1_', averagesNDVIs); percentile10ForTS1 = MathUtils.findPercentile('_NDVI_averages_GeoTIFF_TS1_', sorted, .1).percentile; percentile90ForTS1 = MathUtils.findPercentile('_NDVI_averages_GeoTIFF_TS1_', sorted, .9).percentile; minNDVIForTS1 = minMaxNDVI.min; maxNDVIForTS1 = minMaxNDVI.max; // 注:索引[0]的元素是我们需要的NDVI平均值,波段顺序为:[平均值, 无云样本数, 有云样本数, 无云占比, 有云占比] averagesNDVIs = bandsDataForTS2[0].contents; sorted = averagesNDVIs.sort((a, b) => a-b); minMaxNDVI = MathUtils.findMinMax('_NDVI_averages_GeoTIFF_TS2_', averagesNDVIs); percentile10ForTS2 = MathUtils.findPercentile('_NDVI_averages_GeoTIFF_TS2_', sorted, .1); percentile90ForTS2 = MathUtils.findPercentile('_NDVI_averages_GeoTIFF_TS2_', sorted, .9); minNDVIForTS2 = minMaxNDVI.min; maxNDVIForTS2 = minMaxNDVI.max; })
关键修改点说明
- map回调返回Promise:用
async标记map的回调函数,内部用await处理IO操作,自动返回Promise;如果坚持用.then/.catch,要明确return IOUtils.writeForPath(...).then(...).catch(...)。 - 移除多余的.then链:外层已经是async函数,
await Promise.all之后直接写统计逻辑即可,无需额外嵌套.then,代码更简洁。 - 修复错误处理的变量引用:catch块里不再引用不存在的
ioResults,改用目标文件名标识错误。
内容的提问来源于stack exchange,提问作者Amr
相关产品推荐
相关产品推荐

