You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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等库,但都没有解决问题,请问我遗漏了什么?


解决方案

你遗漏了几个关键的异步处理和响应发送环节,以下是具体问题和修正方案:

核心问题点

  1. 未向客户端发送响应:无论任务成功或失败,必须调用res.send()/res.json()等方法告知客户端结果,否则服务器会一直挂起或提前终止,客户端无法获取响应。
  2. Promise错误处理不完整:
    • 最初版本的processTask中,fs.writeFile的错误未被处理,若写入失败,Promise会一直处于pending状态,Promise.all永远无法完成。
    • 异步版本的processTask捕获错误后仅打印,未重新抛出,导致错误被吞掉,Promise会被标记为resolved,Promise.all会误认为任务已完成。
  3. Fetch的HTTP错误未处理:Fetch仅在网络故障时触发reject,HTTP状态码4xx/5xx不会自动reject,需手动检查并抛出错误。
  4. 异步版本的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 09:39:59