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

嵌套Promise then循环等待最佳实践:无需计数判断全部resolve的方法

核心解决方案

不需要手动计数,JavaScript 原生的Promise.all()就是专门为这类「等待多个并行异步任务全部完成」的场景设计的。
它接收一个由 Promise 实例组成的数组作为入参,会等待数组内所有 Promise 都成功 resolve 后,按传入顺序返回所有 Promise 的执行结果数组;只要数组内任意一个 Promise 抛出错误,就会立即进入异常捕获逻辑,完全可以替代你现在手写的计数判断逻辑。


代码优化实现

你现有代码除了手动计数的问题,还存在两个明显隐患:

  • 直接拼接SQL字符串存在SQL注入风险,pg库原生支持参数化查询,应该优先使用
  • 外层遍历users时没有等待异步逻辑执行完成,如果查询结果包含多个用户,会在第一个用户数据处理完就提前返回接口响应

下面是修正后的实现,首先是基于你原有.then()链式写法的版本:

// 用参数化查询避免SQL注入
const user_query = `SELECT * FROM users WHERE id = $1`;
pg.query(user_query, [id])
  .then((user_result) => {
    const users = user_result.rows;
    // 等待所有用户的关联数据查询完成
    return Promise.all(users.map((user) => {
      user.orgs = [];
      const users_orgs_query = `SELECT org_id FROM users_orgs WHERE user_id = $1`;
      return pg.query(users_orgs_query, [user.id])
        .then((users_orgs_result) => {
          const users_orgs = users_orgs_result.rows;
          // 收集所有org查询的Promise
          const orgQueryPromises = users_orgs.map((user_org) => {
            const orgs_query = `SELECT * FROM orgs WHERE id = $1`;
            return pg.query(orgs_query, [user_org.org_id])
              .then((r) => r.rows[0]);
          });
          // 等所有org查询完成后统一挂载到user对象
          return Promise.all(orgQueryPromises).then(orgs => {
            user.orgs = orgs;
            return user;
          });
        });
    }));
  })
  .then((allUserData) => {
    // 所有用户数据都处理完成后再返回响应
    res.status(200).json(allUserData.length === 1 ? allUserData[0] : allUserData);
  })
  .catch(err => {
    // 统一错误处理
    res.status(500).json({error: err.message});
  });

如果你的运行环境支持async/await(Node.js 8+ 已原生支持),代码可以写得更简洁易读:

async function getUserWithOrgs(req, res) {
  try {
    const id = req.params.id;
    // 1. 查询用户
    const userResult = await pg.query('SELECT * FROM users WHERE id = $1', [id]);
    const users = userResult.rows;
    
    for (const user of users) {
      // 2. 查询关联关系
      const userOrgsResult = await pg.query('SELECT org_id FROM users_orgs WHERE user_id = $1', [user.id]);
      const orgIds = userOrgsResult.rows.map(row => row.org_id);
      
      // 3. 并行查询所有关联org,等待全部完成
      const orgPromises = orgIds.map(orgId => 
        pg.query('SELECT * FROM orgs WHERE id = $1', [orgId]).then(r => r.rows[0])
      );
      user.orgs = await Promise.all(orgPromises);
    }

    res.status(200).json(users.length === 1 ? users[0] : users);
  } catch (err) {
    res.status(500).json({error: err.message});
  }
}

补充说明
  • 如果你需要容忍单个org查询失败不影响整体结果,可以把Promise.all替换为Promise.allSettled,之后过滤掉执行失败的项即可
  • 联表查询确实是性能更优的方案,能减少数据库请求次数,适合数据量较大的场景;如果采用多次查询的方案,Promise.all就是最符合JavaScript设计范式的异步等待实现,完全不需要手动维护计数器。

内容的提问来源于stack exchange,提问作者TrivialCase

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:03:14