ExpressJS问题:无法等待所有Promise完成后再返回响应
问题:POST API无法等待所有异步任务完成再返回响应
我编写了一个POST API,最初代码如下:
app.post('/', async (req: Request, res: Response) => { const tasksRequest = req.body as TasksRequest; let tasks: Promise<any>[] = [] tasks = tasksRequest.tasks.map((t) => processTask(t, tasksRequest.configs)); await Promise.all(tasks); });
对应的processTask函数:
function processTask(task: Task, configs: Configs) { return new Promise<void>((resolve, reject) => { try { const fileName = './output/' + task.tag + 's.json'; fetch(configs.Host + configs.APIsBasePrefix + task.parentResource + task.mostRelatedPath, { method: 'GET' }).then(result => { result.json().then(jsonResult => { fs.writeFile(fileName, JSON.stringify(jsonResult), function () { console.log('finished writing :' + fileName); resolve(); }); }).catch(err => reject(err)); }).catch(err => reject(err)); } catch (err) { console.log(err); } }); }
问题是无法通过await Promise.all(tasks);让服务器等待所有Promise完成后再返回响应。我尝试了改用异步写法:
app.post('/', (req: Request, res: Response) => { const tasksRequest = req.body as TasksRequest; let tasks = [] tasks = tasksRequest.tasks.map( (t) => processTask(t, tasksRequest.configs)); console.log(tasks); Promise.all(tasks).then(res=>{ console.log('After awaiting'); }); }); async function processTask(task: Task, configs: Configs) { try { const fileName = './output/' + task.tag + 's.json'; const result = await fetch(configs.Host + configs.APIsBasePrefix + task.parentResource + task.mostRelatedPath, { method: 'GET' }); const jsonResult = await result.json(); return fs.promises.writeFile(fileName, JSON.stringify(jsonResult)); } catch (err) { console.log(err); } }
还使用过fs.promise等库,但都没有解决问题,请问我遗漏了什么?
解决方案
你遗漏了几个关键的异步处理和响应发送环节,以下是具体问题和修正方案:
核心问题点
- 未向客户端发送响应:无论任务成功或失败,必须调用
res.send()/res.json()等方法告知客户端结果,否则服务器会一直挂起或提前终止,客户端无法获取响应。 - Promise错误处理不完整:
- 最初版本的
processTask中,fs.writeFile的错误未被处理,若写入失败,Promise会一直处于pending状态,Promise.all永远无法完成。 - 异步版本的
processTask捕获错误后仅打印,未重新抛出,导致错误被吞掉,Promise会被标记为resolved,Promise.all会误认为任务已完成。
- 最初版本的
- Fetch的HTTP错误未处理:Fetch仅在网络故障时触发reject,HTTP状态码4xx/5xx不会自动reject,需手动检查并抛出错误。
- 异步版本的API路由未正确等待任务完成:第二个版本的路由未用
async/await,且Promise.all().then()中未发送响应,任务完成后客户端仍收不到结果。
修正后的代码
方案一:修复原始Promise写法
修正processTask(处理所有错误)
function processTask(task: Task, configs: Configs) { return new Promise<void>((resolve, reject) => { try { const fileName = './output/' + task.tag + 's.json'; fetch(configs.Host + configs.APIsBasePrefix + task.parentResource + task.mostRelatedPath, { method: 'GET' }) .then(result => { if (!result.ok) { throw new Error(`请求失败,状态码: ${result.status}`); } return result.json(); }) .then(jsonResult => { fs.writeFile(fileName, JSON.stringify(jsonResult), function (err) { if (err) { console.error('写入文件失败:', err); reject(err); return; } console.log('finished writing :' + fileName); resolve(); }); }) .catch(err => { console.error('任务执行失败:', err); reject(err); }); } catch (err) { console.error('同步错误:', err); reject(err); } }); }
修正API路由(添加响应发送)
app.post('/', async (req: Request, res: Response) => { try { const tasksRequest = req.body as TasksRequest; const tasks = tasksRequest.tasks.map(t => processTask(t, tasksRequest.configs)); await Promise.all(tasks); res.status(200).json({ message: '所有任务执行完成' }); } catch (err) { console.error('批量任务失败:', err); res.status(500).json({ error: '任务执行失败', details: (err as Error).message }); } });
方案二:修复异步函数写法
修正processTask(捕获错误后抛出,处理HTTP错误)
async function processTask(task: Task, configs: Configs) { try { const fileName = './output/' + task.tag + 's.json'; const result = await fetch(configs.Host + configs.APIsBasePrefix + task.parentResource + task.mostRelatedPath, { method: 'GET' }); // 处理HTTP错误状态码 if (!result.ok) { throw new Error(`请求失败,状态码: ${result.status}`); } const jsonResult = await result.json(); await fs.promises.writeFile(fileName, JSON.stringify(jsonResult)); console.log('finished writing :' + fileName); } catch (err) { console.error('任务失败:', err); throw err; // 必须抛出错误,让Promise.all捕获 } }
修正API路由(用async/await等待任务,发送响应)
app.post('/', async (req: Request, res: Response) => { try { const tasksRequest = req.body as TasksRequest; const tasks = tasksRequest.tasks.map(t => processTask(t, tasksRequest.configs)); await Promise.all(tasks); console.log('After awaiting'); res.status(200).json({ message: '所有任务执行完成' }); } catch (err) { console.error('批量任务失败:', err); res.status(500).json({ error: '任务执行失败', details: (err as Error).message }); } });
内容的提问来源于stack exchange,提问作者Mu'men Tayyem
相关产品推荐
相关产品推荐

