優(yōu)雅解決按鈕”重復(fù)點(diǎn)擊“問(wèn)題

一、這個(gè)問(wèn)題怎么解決呢?
let clickButton = (function () {let lock = falsereturn function (postParams) {if (lock) returnlock = true// 假設(shè)使用axios發(fā)送請(qǐng)求axios.post('urlxxx', postParams).then(// 表單提交成功).catch(error => {// 表單提交出錯(cuò)console.log(error)}).finally(() => {// 不管成功失敗 都解鎖lock = false})}})()button.addEventListener('click', clickButton)
當(dāng)然對(duì)于button按鈕,可以使用setAttribute('disabled', xxx)和removeAttribute('disabled')來(lái)代替lock標(biāo)記。
這個(gè)方案問(wèn)題在于,對(duì)于每一次按鈕點(diǎn)擊,我們都要寫個(gè)lock標(biāo)記,相當(dāng)于重復(fù)的邏輯會(huì)出現(xiàn)在代碼的各個(gè)地方——是不是可以封裝一下呢?
二、封裝按鈕鎖定、解鎖邏輯
寫一個(gè)裝飾器將邏輯封裝起來(lái):
function ignoreMultiClick(func, manual = false) {let lock = falsereturn function (...args) {if (lock) returnlock = truelet done = () => (lock = false)if (manual) return func.call(this, ...args, done)let promise = func.call(this, ...args)Promise.resolve(promise).finally(done)return promise}}
將想監(jiān)聽(tīng)點(diǎn)擊回調(diào)函數(shù)func作為傳遞給ignoreMultiClick進(jìn)行裝飾,會(huì)返回一個(gè)新的函數(shù),使用該函數(shù)作為點(diǎn)擊的回調(diào)事件即可。
這里同樣用了一個(gè)標(biāo)記lock來(lái)上鎖,有兩種方法解鎖:
手動(dòng)解鎖:可以給ignoreMultiClick傳遞一個(gè)參數(shù)manual,意思是主動(dòng)調(diào)用解鎖。若該參數(shù)為truthy,則點(diǎn)擊事件觸發(fā)時(shí)會(huì)給原始的點(diǎn)擊回調(diào)func傳遞一個(gè)參數(shù)done,done是一個(gè)函數(shù),調(diào)用它可以解鎖。
自動(dòng)解鎖:可以使原監(jiān)聽(tīng)函數(shù)func返回一個(gè)promise,在該promise決議后自動(dòng)執(zhí)行解鎖操作。因?yàn)镻romise管理回調(diào)函數(shù)非常方便,并且像axios這樣非常常用的請(qǐng)求庫(kù)返回值本身也是一個(gè)promise,所以默認(rèn)情況使用這種方式。
當(dāng)然返回promise并不是必須的,有時(shí)候我們?cè)诎l(fā)請(qǐng)求前會(huì)進(jìn)行一些驗(yàn)證,驗(yàn)證沒(méi)通過(guò)則直接return,此時(shí)裝飾器函數(shù)也能正常處理,因?yàn)槭褂肞romise.resolve包裹了一下promise:Promise.resolve(promise).finally(done)。
三、使用實(shí)例
自動(dòng)解鎖使用例子:
let clickButton = ignoreMultiClick(function (postParams) {if (!checkForm()) return // 假設(shè)有一些檢測(cè)表單的操作,檢查不通過(guò)則直接返回// 返回promisereturn axios.post('urlxxx', postParams).then(// 表單提交成功).catch(error => {// 表單提交出錯(cuò)console.log(error)})})button.addEventListener('click', clickButton)
手動(dòng)解鎖:
let clickButton = ignoreMultiClick(function (postParams, done) {if (!checkForm()) return done() // 表單驗(yàn)證不通過(guò)解鎖axios.post('urlxxx', postParams).then(// 表單提交成功).catch(error => {// 表單提交出錯(cuò)console.log(error)}).finally(() => done()) // 請(qǐng)求結(jié)束解鎖})button.addEventListener('click', clickButton)
普通場(chǎng)景下還是自動(dòng)解鎖比較簡(jiǎn)單,因?yàn)榭赡苡卸鄠€(gè)條件分支,手動(dòng)解鎖需要在每一個(gè)返回的地方都調(diào)用done。
