这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战
前言
今天遇到一个问题,我的项目的场景是,页面上有按钮,按钮可以切换,切换后底下的内容会切换,但是这个内容是请求后台,后台返回的数据。
如果我切换按钮的速度快一些,而且先请求的接口的返回比后请求接口的返回慢一些,就会导致先请求的接口返回的内容展示在后请求的按钮下面,导致内容不一致。
场景复现
下面我用代码模拟一下:
<div>
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
</div>
<div class="content">content:</div>
<script>
const buttonList = document.querySelectorAll('button')
const contentDom = document.querySelector('.content')
const content = contentDom.innerText
buttonList.forEach((item, i) => {
item.addEventListener('click', () => {
const index = i + 1
if (item.innerText === 'button 1') {
setTimeout(() => {
freshContent(item, index)
}, 2000)
}
else if (item.innerText === 'button 2') {
setTimeout(() => {
freshContent(item, index)
}, 500)
} else {
freshContent(item, index)
}
})
})
function freshContent(index) {
contentDom.innerText = content + ' ' + index
}
</script>
解析:
这里我用setTimeout模拟接口的返回速度,点击button1,是2000ms后刷新内容,点击button2,是500ms后刷新内容,点击button3,是直接刷新内容。
效果如下:
可以看到,我先点击的第一个按钮,最后点击的第二个按钮,但是由于第一个按钮的刷新是在2000ms,就是我点击第二个按钮和第二个按钮的内容刷新之后才会触发,导致把第二个按钮的内容给覆盖了,出现了点击第二个按钮,然后展现第一个按钮的内容的情况。
那怎么解决呢?
问题解决
第一种方法
通过记录次数来解决。
每次按钮点击的时候都记录次数(次数加一),然后把当前的次数都存到点击的按钮身上,刷新内容的时候看记录的次数和当前的按钮的次数是不是相等的。如果是相等的,则证明是最后一次触发的,就可以刷新内容。
代码如下:
<div>
<button>button 1</button>
<button>button 2</button>
<button>button 3</button>
</div>
<div class="content">content:</div>
<script>
const buttonList = document.querySelectorAll('button')
const contentDom = document.querySelector('.content')
const content = contentDom.innerText
// 记录的次数
let count = 0
buttonList.forEach((item, i) => {
item.addEventListener('click', () => {
// 次数加一
count++
// 把当前次数存到当前button中
item.count = count
const index = i + 1
if (item.innerText === 'button 1') {
setTimeout(() => {
freshContent(item, index)
}, 2000)
}
else if (item.innerText === 'button 2') {
setTimeout(() => {
freshContent(item, index)
}, 500)
} else {
freshContent(item, index)
}
})
})
function freshContent(item, index) {
// 判断当前button的次数和记录的次数是否相等
if (item.count === count) {
contentDom.innerText = content + ' ' + index
}
}
</script>
可以看到不管怎么点,内容都是一致的。
第二种方法
可以通过中断请求来实现。
就是我们在点击下一个按钮的时候,把上一次按钮的请求中断了,XMLHttpRequest可以通过abort方法实现,axios可以通过cancelToken实现。
不过这个方法不确定是否请求已经发送给后台了,所以有可能会中断失败。
第三种方法
可以通过加loading动画展示,直到请求返回,才可点击下一个按钮。
就是我们在点击按钮的时候,发出请求后,加一个loading的动画,然后此时按钮不可点,直到请求成功了,或者失败了,才把loading去掉,按钮才可点击,依此类推。
总结
这个就是我遇到的开发中按钮切换的竞态问题,这种问题很常见,大家记录下来,后面遇到就知道怎么解决了这个问题了。
感谢你们的阅读。