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

如何结合Promises与forEach使用Firebase Cloud Functions及排查超时问题

解决FCM通知超时与Promise/forEach的坑

嘿,兄弟,你这个问题我太熟了!之前做批量推送的时候也踩过一模一样的forEach+Promise的坑,导致偶尔出现Error: fcm.googleapis.com network timeout. Please try again的错误。复制模块没问题是因为它的操作是本地数据处理(复制ref到员工ID引用),没有网络请求或者是低频次的数据库操作,不存在并发超时的问题,所以一直稳定。

问题根源

forEach根本不关心内部的Promise!它会一口气遍历所有员工,瞬间发起所有FCM请求——相当于同时打N个HTTP请求到FCM服务器。FCM有并发限制,或者你的服务器/客户端的网络连接数不够,就会出现部分请求超时。

解决方案

我给你几个递进的方案,从简单到进阶:

1. 用for...of替代forEach,串行处理请求

把批量请求改成逐个发起,虽然慢一点,但绝对不会因为并发太高超时:

// 原来的错误写法(forEach不等待Promise完成)
employees.forEach(async (employee) => {
  await sendFCMNotification(employee);
});

// 修改后的正确写法(for...of会等待每个Promise完成)
for (const employee of employees) {
  try {
    await sendFCMNotification(employee);
    // 可选:加个小延迟,避免请求太密集触发限流
    await new Promise(resolve => setTimeout(resolve, 100));
  } catch (err) {
    console.error(`给员工${employee.id}发通知失败:`, err);
    // 这里可以加简单的重试逻辑
  }
}

2. 带并发限制的批量处理(平衡速度与稳定性)

如果串行处理太慢,你可以控制同时发起的请求数量,比如一次发5个:

async function processInBatches(array, batchSize, handler) {
  const batches = [];
  // 把员工数组分成多个批次
  for (let i = 0; i < array.length; i += batchSize) {
    batches.push(array.slice(i, i + batchSize));
  }

  // 逐个处理批次,每个批次内并行请求
  for (const batch of batches) {
    await Promise.all(
      batch.map(async (employee) => {
        try {
          await handler(employee);
        } catch (err) {
          console.error(`处理员工${employee.id}失败:`, err);
        }
      })
    );
    // 批次之间加延迟,给服务器喘口气
    await new Promise(resolve => setTimeout(resolve, 500));
  }
}

// 使用方式
await processInBatches(employees, 5, sendFCMNotification);

3. 给FCM请求加超时和重试机制

偶尔的网络波动也会导致超时,给请求加超时控制和自动重试:

async function sendFCMNotificationWithRetry(employee, retries = 3) {
  try {
    // 给请求加超时限制,比如10秒
    const timeoutPromise = new Promise((_, reject) => 
      setTimeout(() => reject(new Error("FCM请求超时")), 10000)
    );
    // 用Promise.race确保请求不会无限挂起
    await Promise.race([
      sendFCMNotification(employee),
      timeoutPromise
    ]);
  } catch (err) {
    if (retries > 0 && err.message.includes("timeout")) {
      console.log(`给员工${employee.id}发通知超时,重试中...剩余次数: ${retries-1}`);
      await sendFCMNotificationWithRetry(employee, retries - 1);
    } else {
      throw err;
    }
  }
}

把这些方案结合起来,你的通知模块超时问题应该就能解决了!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:56:19