背景:
最近參與開發的小程序,涉及到即時消息(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都可以正常發送消息,無消息重復發送情況發生了
