一、问题怎么解决& [# q" T7 A A! B9 L2 x3 E: ?
简单点,使用一个lock标记,在请求发出时上锁,上锁后就不可以再发请求,可以在请求结束后解锁:% Z; g7 M. l) e, C5 D* N: T: @$ G
-
- let clickButton = (function () {
- let lock = false
- return function (postParams) {
- if (lock) return
- lock = true
- // 假设使用axios发送请求
- axios.post('urlxxx', postParams).then(
- // 表单提交成功
- ).catch(error => {
- // 表单提交出错
- console.log(error)
- }).finally(() => {
- // 不管成功失败 都解锁
- lock = false
- })
- }
- })()
-
- button.addEventListener('click', clickButton)
当然对于button按钮,可以使用setAttribute('disabled', xxx)和removeAttribute('disabled')来代替lock标记。这个方案问题在于,对于每一次按钮点击,我们都要写个lock标记,相当于重复的逻辑会出现在代码的各个地方——是不是可以封装一下呢?
" p! o& w7 @/ Q# g9 z: K" T2 r二、封装按钮锁定、解锁逻辑" |+ w7 v& m5 r& }0 O n. U4 L' e
写一个装饰器将逻辑封装起来: ) L; g: U2 Z, |0 M' }/ G5 ?9 S- v
- function ignoreMultiClick(func, manual = false) {
- let lock = false
- return function (...args) {
- if (lock) return
- lock = true
- let done = () => (lock = false)
- if (manual) return func.call(this, ...args, done)
- let promise = func.call(this, ...args)
- Promise.resolve(promise).finally(done)
- return promise
- }
- }
将想监听点击回调函数func作为传递给ignoreMultiClick进行装饰,会返回一个新的函数,使用该函数作为点击的回调事件即可。
' d5 a8 L1 k: `& j5 X2 ] 这里同样用了一个标记lock来上锁,有两种方法解锁: $ d* T3 \/ ^8 D4 y* b8 B1 B
手动解锁:可以给ignoreMultiClick传递一个参数manual,意思是主动调用解锁。若该参数为truthy,则点击事件触发时会给原始的点击回调func传递一个参数done,done是一个函数,调用它可以解锁。
; A3 v& u) b ^( h: y自动解锁:可以使原监听函数func返回一个promise,在该promise决议后自动执行解锁操作。因为Promise管理回调函数非常方便,并且像axios这样非常常用的请求库返回值本身也是一个promise,所以默认情况使用这种方式。 ' ^1 X0 A- w# W- ^- D$ p5 Z- g8 g
当然返回promise并不是必须的,有时候我们在发请求前会进行一些验证,验证没通过则直接return,此时装饰器函数也能正常处理,因为使用Promise.resolve包裹了一下promise:Promise.resolve(promise).finally(done)。 - H- g% t1 L6 R8 \7 J+ M! n3 ]
三、使用实例) w, K+ d: e7 N, C+ R
自动解锁使用例子: 4 c* s; }% l: t: [3 M5 j
- let clickButton = ignoreMultiClick(function (postParams) {
- if (!checkForm()) return // 假设有一些检测表单的操作,检查不通过则直接返回
- // 返回promise
- return axios.post('urlxxx', postParams).then(
- // 表单提交成功
- ).catch(error => {
- // 表单提交出错
- console.log(error)
- })
- })
- button.addEventListener('click', clickButton)
手动解锁使用例子:
6 r0 |' h g. r9 ^9 ~' _# {; F0 o-
- let clickButton = ignoreMultiClick(function (postParams, done) {
- if (!checkForm()) return done() // 表单验证不通过解锁
- axios.post('urlxxx', postParams).then(
- // 表单提交成功
- ).catch(error => {
- // 表单提交出错
- console.log(error)
- }).finally(() => done()) // 请求结束解锁
- })
- button.addEventListener('click', clickButton)
普通场景下还是自动解锁比较简单,因为可能有多个条件分支,手动解锁需要在每一个返回的地方都调用done。 |