背景:
最近参与开发的小程序,涉及到即时消息(IM)发送的功能;
聊天界面如下,通过键盘上的【发送】按钮,触发消息发送功能
问题发现:
功能开发完毕,进入测试流程;测试工程师反馈说:
在Android手机上,在极短的时间内频繁点击键盘上的【发送】按钮,消息会重复发送;IOS上该问题不太明显
本以为是普通的防重复提交问题,于是自然想到通过设定flag/js加锁的方式解决该问题,于是开始优化代码:
项目基本代码:
wxml:
<input type="text" value="{{msgValue}}" confirm-type="send" bindconfirm="sendMsg" bindinput="bindKeyInput" placeholder="请输入聊天内容" />
JS:
bindKeyInput(e) { this.setData({ msgValue: e.detail.value.replace(/^\s+|\s+$/g, "") }); }, sendMsg() { let self = this; let msg = self.data.msgValue; if (msg && self.data.sendMsgState) { self.data.sendMsgState = false app.globalData.nim.sendText({ scene: \'p2p\', to: self.data.doctorId, text: msg, done(error, msg) { if (!error) { //消息发送成功 self.setData({ msgValue: \'\' }) } else { //消息发送失败 wx.showToast({ title: \'消息发送失败,请稍后再试\', icon: \'none\', duration: 1500, mask: true }) } } }) } }
1# 设定flag/js加锁
//在页面初始数据data中,声明“锁”: sendMsgState data: { sendMsgState: true } //在发送消息方法中,符合消息发送条件的时候,把sendMsgState的值置为false; //并在消息发送成功之后,将消息发送框的value置空的之后,将sendMsgState设为true sendMsg() { let self = this; let msg = self.data.msgValue; if (msg && self.data.sendMsgState) { self.data.sendMsgState = false app.globalData.nim.sendText({ scene: \'p2p\', to: self.data.doctorId, text: msg, done(error, msg) { if (!error) { //消息发送成功,置空输入框;然后把sendMsgState重新设置为true self.setData({ msgValue: \'\' }, () => { self.data.sendMsgState = true }) } else { //消息发送失败 wx.showToast({ title: \'消息发送失败,请稍后再试\', icon: \'none\', duration: 1500, mask: true }) } } }) } }
测试结果:
Android手机上依然存在该问题,且很容易复现。
分析原因:
在极短的时间内,频繁点击键盘上的发送按钮;此时:锁(sendMsgState)还没来得及置为false,发送内容输入框的值还没有被清空;
但发送事件已经被有效触发多次,导致了发送消息的重复。
2# 在方案一设定flag/js加锁的基础上,增加连续点击按钮事件间隔少于1s,或者连续两次发送内容相同都停止发送的补充规则
2.1:增加连续点击按钮事件间隔少于1s
经验证:正常的消息发送使用流程,连续两次的消息发送间隔都是超过1s的;间隔小于1s的行为,可判定为重复提交:
具体做法:
步骤一:在data中注册lastSendTime,设置值为空;触发发送事件sendMsg的时候,把当前时间保存到变量currentTime;
步骤二:判断当前时间currentTime与上次发送时间的差值是否小于1000;如果是,则发送事件连续触发时间短于1s,停止发送;
步骤三:消息发送成功之后,在置空内容输入框的setData回调方法中,将lastSendTime的值更新为:currentTime;
2.2:如果当前发送的消息内容和上一次保存在data中的msgValue相同,则可判断连续两次消息重复
因为每次发送成功,data中msg都会被置空;而内容为空的时候,又是不允许发送的;
所以,在短时间内,如果当前发送的消息内容和上一次保存在data中的msgValue相同,则可判断连续两次消息重复
最终优化方案:
sendMsg() { let self = this; let msg = self.data.msgValue; // 防止两次点击操作间隔太快 let currentTime = new Date(); if ((currentTime - this.data.lastSendTime < 1000) || (msg === self.data.msg)) { //发送事件连续触发时间短于1s,或连续两次发送内容相同,则返回 return; } if (msg && self.data.sendMsgState) { self.data.sendMsgState = false app.globalData.nim.sendText({ scene: \'p2p\', to: self.data.doctorId, text: msg, done(error, msg) { if (!error) { self.setData({ msgValue: \'\' }, () => { self.data.sendMsgState = true self.data.lastSendTime = currentTime }) } else { //消息发送失败 wx.showToast({ title: \'消息发送失败,请稍后再试\', icon: \'none\', duration: 1500, mask: true }) } } }) } }
综上所述:
在单一的flag/js加锁无效的情况下;通过添加额外的规则补充校验,最终方案如下:
在发送内容msg有效及flag/js锁为true的基础上;发送事件sendMsg连续两次触发时间间隔大于或等于1s,及连续两次发送内容不相同的情况下,才允许消息被发送;
最终测试结果:无论是Android,还是IOS都可以正常发送消息,无消息重复发送情况发生了