如何异步调用OneSignal推送通知并批量发送至各设备
Hey there! I get why that manual 2-second wait with closures didn't work—regular loops don't pause for async operations like setTimeout, so you probably ended up firing all your push requests almost at once, or ran into scope issues with the loop variable. Let's fix this properly using modern JavaScript async patterns.
首先,假设你的推送通知函数是异步的
Let's say you have an async function to send the push notification, like this:
async function sendPushNotification(deviceId, userData) { // 这里是调用推送服务的逻辑,比如调用API const response = await fetch('/api/send-push', { method: 'POST', body: JSON.stringify({ deviceId, userData }) }); return response.json(); }
方案1:顺序发送(逐个处理,前一个完成再发下一个)
如果你需要确保通知按顺序发送(比如避免 overwhelming 推送服务),用 for...of 配合 await 是最直接的方式:
const userinfo = [ { userdata: "some info for user 1", deviceid: "user device id 1" }, { userdata: "some info for user 2", deviceid: "user device id 2" }, // 更多用户... ]; async function processAllNotifications() { for (const user of userinfo) { try { const result = await sendPushNotification(user.deviceid, user.userdata); console.log(`推送成功给用户:${user.userdata}`, result); } catch (error) { console.error(`推送失败给用户:${user.userdata}`, error); // 可选:处理错误,比如重试或者记录日志 } // 如果需要在每次推送后等待一段时间(比如避免限流),可以加个延迟 // await new Promise(resolve => setTimeout(resolve, 2000)); } } // 调用函数 processAllNotifications();
方案2:并行发送(同时发送所有,等待全部完成)
如果推送服务支持并发,并且你想提高效率,可以用 Promise.all 一次性发送所有请求:
async function processAllNotificationsInParallel() { const promises = userinfo.map(user => sendPushNotification(user.deviceid, user.userdata) .then(result => ({ success: true, user, result })) .catch(error => ({ success: false, user, error })) ); const results = await Promise.all(promises); // 处理结果 results.forEach(result => { if (result.success) { console.log(`推送成功给用户:${result.user.userdata}`, result.result); } else { console.error(`推送失败给用户:${result.user.userdata}`, result.error); } }); } processAllNotificationsInParallel();
为什么你的闭包方法没生效?
通常用 setTimeout 加闭包的问题在于,如果你用了普通的 for 循环(不是 let 声明变量),循环变量会共享同一个引用,导致所有超时函数都拿到最后一个用户的数据。比如:
// 错误示例: for (var i = 0; i < userinfo.length; i++) { setTimeout(function() { // 这里的userinfo[i]会是undefined或者最后一个元素,因为循环已经结束了 sendPushNotification(userinfo[i].deviceid, userinfo[i].userdata); }, 2000 * i); }
如果要让这种方式生效,需要用 let 声明循环变量(ES6+)或者创建闭包捕获每个迭代的变量:
// 修复后的闭包方式(不推荐,不如async/await清晰) for (let i = 0; i < userinfo.length; i++) { setTimeout(function() { const user = userinfo[i]; sendPushNotification(user.deviceid, user.userdata); }, 2000 * i); }
但显然 async/await 写法更易读和维护,所以首选前面的方案。
内容的提问来源于stack exchange,提问作者U rock

