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