如何结合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
相关产品推荐
相关产品推荐

