Discord.js PresenceUpdate事件多次触发,如何获取最新完整用户状态?
如何避免Discord.js的PresenceUpdate事件重复触发并获取完整状态对象?
问题描述
我用这段代码监听Discord.js的PresenceUpdate事件:
client.on(Events.PresenceUpdate, async (_, newUser )=>{ console.log(newUser.user.id + " " + newUser.activities[0]); });
但每次修改状态(比如自定义消息、游戏状态)时,这个事件会触发3-5次甚至更多,每次返回的newUser对象都略有不同。我只想获取包含用户最新完整状态的对象,之前试过用存储ID和布尔值的Map来处理,但只能拿到数据不全的第一个对象,不是最新的完整状态。也考虑过事件触发后等待1秒再手动获取当前状态,但这样还是会多次触发事件。请问有没有简便的解决方法?能不能区分无效触发和包含最新状态的有效newUser对象?
解决方案:用防抖函数合并多次触发
首先得明白为啥会多次触发:Discord推送用户状态的时候不是一次性把所有信息发过来的,而是分批次同步——比如先更新在线状态,再同步游戏活动,最后推送自定义状态,所以PresenceUpdate事件会跟着触发好几次。
最简便的解决办法就是用防抖(Debounce)函数:它能让你的处理逻辑延迟执行,等短时间内的所有触发都结束后,再执行一次,这样就能拿到最终的完整状态对象。
步骤1:实现防抖函数
先写一个通用的防抖工具函数:
function debounce(func, delay) { let timeoutId; return (...args) => { // 每次触发时清除之前的定时器 clearTimeout(timeoutId); // 重新设置定时器,延迟执行目标函数 timeoutId = setTimeout(() => func.apply(this, args), delay); }; }
步骤2:用防抖包裹事件处理逻辑
把你的状态处理逻辑用防抖函数包裹,延迟时间建议设为500ms(可以根据实际情况调整,比如如果状态更新慢就加到800ms):
// 先导入需要的类型(如果用到的话) const { ActivityType } = require('discord.js'); // 防抖后的处理函数,500ms内多次触发只会执行最后一次 const handlePresenceUpdate = debounce(async (_, newUser) => { // 这里就能拿到完整的最新状态了 console.log(`${newUser.user.id} 的最新活动:`, newUser.activities[0]); // 如果你需要精准判断特定状态是否完整,可以加个检查 // 比如判断自定义状态是否存在且完整 const customStatus = newUser.activities.find(act => act.type === ActivityType.CustomStatus); if (customStatus && customStatus.state) { console.log(`${newUser.user.tag} 的自定义状态:${customStatus.state}`); } }, 500); // 监听事件,调用防抖后的函数 client.on(Events.PresenceUpdate, handlePresenceUpdate);
这样一来,不管事件触发多少次,只有当最后一次触发后过了500ms,才会执行你的处理逻辑,此时用户的状态已经完全同步,newUser就是包含所有信息的最新对象。
额外小技巧:判断状态是否完整
如果你不想完全依赖防抖,还可以在处理函数里加一些判断,确保拿到的是有效状态:
- 比如检查
newUser.activities里的目标活动类型是否存在,且关键字段(比如state自定义状态文本、name游戏名称)不为空 - 或者对比当前状态和上一次存储的状态,只有当状态确实发生了完整变化时才处理
不过防抖已经能解决大部分场景,结合字段检查会更稳妥。
内容的提问来源于stack exchange,提问作者Barni
相关产品推荐
相关产品推荐

