如何在Promise.all的async map调用中终止PDF扫描任务
解决方案
问题核心在于:原代码中最耗时的扫描页面阶段(Promise.all+map)完全没有添加AbortSignal检查,导致点击取消后这些异步任务仍会继续执行;同时后续任务的检查逻辑只是提前返回,没有终止整个Promise链。
1. 在扫描页面的异步任务中添加终止检查并中断Promise链
修改扫描阶段的代码,在每个页面处理前检查信号状态,若已终止则抛出错误并reject整个流程:
setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.SCANNING')); try { await Promise.all( Array.from({ length: pdf._pdfInfo.numPages }).map(async (x, i) => { // 每次处理页面前检查是否已取消 if (abortControllerSignal.aborted) { throw new Error('SCAN_ABORTED'); } const cropCordsConfig = await configService.get('ocr-position'); const cords = { x: cropCordsConfig?.x || cropCords.x, y: cropCordsConfig?.y || cropCords.y, width: cropCordsConfig?.width || cropCords.width, height: cropCordsConfig?.height || cropCords.height, }; // 读取图片前再次检查 if (abortControllerSignal.aborted) { throw new Error('SCAN_ABORTED'); } const pageImage = await pdfService.readAsImageData(pdf, i + 1, cords); await engine.loadImage(pageImage); const text = engine.getText(); pageCodes.push({ code: text.replace(/\n/gi, ''), pageIndex: i, }); setProgress(parseInt((pageCodes.length / pdf._pdfInfo.numPages) * 100)); }) ); } catch (err) { if (err.message === 'SCAN_ABORTED') { setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.CANCELLED')); setProgress(0); reject(err); // 终止整个Promise,不再执行后续逻辑 return; } // 处理其他扫描错误 throw err; }
2. 给支持AbortSignal的异步方法传递信号
Node.js的fs.promises方法支持接收signal参数,传递AbortSignal后,调用abort时会直接中断文件读取操作:
// 示例:修改文件读取代码 const newExtractedPdf = await pdfService.extractPagesToPdf({ file: await fs.promises.readFile(pdfPath, { signal: abortControllerSignal }), pages: docPages .sort( (a, b) => parseInt(parseCode(a.code, pageLength).pageNo) - parseInt(parseCode(b.code, pageLength).pageNo) ) .map((p) => ({ originalPageNumber: parseInt(p.pageIndex) + 1 })), });
3. 优化后续任务的终止逻辑
后续创建PDF的任务中,检查到abort后不仅要返回,还要确保整个Promise链终止。可以将return改为抛出终止错误,统一在外层catch中处理:
const docPromises = Object.keys(documents).map(async (docId) => { if (abortControllerSignal.aborted) { throw new Error('SCAN_ABORTED'); } // 剩余逻辑不变... }); try { await Promise.all(docPromises); } catch (err) { if (err.message === 'SCAN_ABORTED') { setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.CANCELLED')); setProgress(0); reject(err); return; } throw err; }
通过以上修改,就能在点击取消按钮后立即终止所有异步任务,不再继续执行后续的文件读取和处理逻辑。
内容的提问来源于stack exchange,提问作者Qasim Ali
相关产品推荐
相关产品推荐

