前端频繁请求接口

PainAndLove
  • 358

在做一个vue项目的时候发现一个问题,就是有一个根据input搜索框的值来查询数据的页面。
在快速点击搜索按钮2次或者多次之后。由于网咯的原因。可能导致最后一次点击查询按钮返回的值被前面的请求结果给覆盖了。。这种情况应该怎么解决呢。。只想保留最新一次发起的请求。

如果没有这个搜索按钮。只有一个input框(实时搜索)呢?即便是函数截流也会导致这个覆盖的问题(网络延迟)

回复
阅读 11.8k
14 个回答

一、对于前端

防止重复点击按钮:可以采用一点击一次后让按钮置灰,暂时不可以用,响应结束后再可以用。
防止重复提交请求:可以采用请求队列的方式,每次请求时检查队列中有无该请求,有则返回,无则提交,并将该请求添加到队列,响应完毕,将响应的请求从队列中移除。

二、安全方面,需要前后端协同处理

推荐您看一下说说API的防重放机制

目前我们公司就采用这些方式!

设置一个id,每次开始ajax请求前更新这个id并在scope中保留副本,并取消前次ajax请求。请求结束后查询该id是否与本次请求的id对应,否的话就抛弃本次response
节流函数适合避免短时间内多次请求,但是并不能保证请求返回后按顺序更新UI

发条橙子
  • 394

想了一个侵入性不是很强的的方式,现在大部分项目或库都基于Promise封装的ajax请求,Promise的安全性问题又使得自身的状态无法在外部改变。

我的思路是使用一个warp高阶函数,兜所有请求,如果是在队列最后一个请求成功了才算成功,同时清除队列。

[请求1, 请求2, 请求3, 请求4]
function warp(ajax) {
  let stack = []
  return function () {
    let req
    let self = this
    let length = stack.length + 1
    let p = new Promise((reslove, reject) => {
      req = ajax.call(self, ...arguments).then(data => {
        if (stack.length === length) {
          // 最新的请求 就是你
          stack = []
          reslove(data)
        } else {
          // 如果是过期的请求就reject 或者随意
          reject('old request')
        }
      }) 
    })
    stack.push(req)
    return p
  }
}
// 假定一个普通的ajax请求
let ajax = () => {
    return new Promise((reslove, reject) => {
        setTimeout(() => reslove('success'), 3000)
    })
}
// 封装过的特殊ajax请求
let getSomeAjax = warp(ajax)

// 发起请求
getSomeAjax().then(console.log, console.err) 
getSomeAjax().then(console.log, console.err)

锁,点击后,立刻disable掉搜索按钮,结果出来后在去掉disable。或者,程序中加个锁。

防止重复点击按钮,获取到想要的数据以后,禁用按钮,这样还可以节省流量

ezio
  • 2
新手上路,请多包涵

加个开关就行了,点击请求的时候判断开关,

使用rxjs的debounceTime操作符

请求的时候加个时间戳,然后比较就好了吧。

这个貌似挺简单,点击搜索按钮,判断是否有搜索值,没有提示搜索值不能为空。有值,显示一个loading的遮罩,发送请求,请求回来了遮罩取消,继续其他的逻辑。这里,遮罩显示后,能阻止页面上的点击操作,不需要复杂的跟踪,用户体验也不错。

如果是移动端的话,我想到了一个比较简单的方法,你可以设置两个按钮,第一个按钮加上点击事件,并且默认显示,第二个按钮不加事件,当用户点击之后请求接口马上隐藏第一个按钮,并显示第二个按钮。如果是基于vue开发,可以用v-show去实现这个效果,虽然这样可以避免用户多次请求数据接口,但是这方法还是没有大神Xeira和iboy的规范。。。

函数节流思路

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏