解决并行发起多次请求,响应顺序不一致的问题

180 阅读2分钟

在React类组件中,经常需要在componentDidUpdate、componentWillReceiveProps判断各种属性的变化触发某个请求,在Vue中也经常会用一个watch监听若干变量,触发某个请求

在这些情况当中,请求通常会并行触发很多次,但发出去的请求是不是按照顺序回来,就无法保证了,因此回调执行的顺序就会出现问题,通常情况下我们需要的是最新的那次回调回来的数据

可以通过闭包的方式,为每次函数的调用创建一个变量,建立起一个对应的关联关系来

方法一:

class CustomForm {
  ...
  componentDidUpdate = (prevProps) => {
    if (prevProps.initialValues.nid !== this.props.initialValues.nid) {
      // fetchMetrics可能会请求多次
      this.fetchMetrics(0)
    }
  }

...
// 在较高一级作用域上添加一个变量,用来跟踪最新走到了第几次
CustomForm.fetchMetricsExceTimes = 0
  async fetchMetrics(category = 1, value) {
    // 每调用一次fetchMetrics,就将全局变量加1
    CustomForm.fetchMetricsExceTimes++
    // 同时,新建一个局部变量,来和本次调用形成映射关系
    let curFetchMetricsExecIndex = CustomForm.fetchMetricsExceTimes
    
    // 进行了一些异步操作
    await xxx
    
    // 每次请求回来的顺序可能不同,如果满足下面的条件,则证明先发出去的请求后回来了,直接忽略,避免走到下面的setState,将错误的数据展示到页面
    if (curFetchMetricsExecIndex < CustomForm.fetchMetricsExceTimes) return
    
    // 将正确的数据设置上去
    this.setState({ metrics });

方法二:

在每一次触发请求时,都打一个标记,标记这次这次请求的结果应不应该用来更新页面,每次请求进来时都将上次发起的请求对应的标记置为false:

class CustomForm {
  ...
  componentDidUpdate = (prevProps) => {
    if (prevProps.initialValues.nid !== this.props.initialValues.nid) {
      // fetchMetrics可能会请求多次
      this.fetchMetrics(0)
    }
  }

...
// 用来存放本次请求结果应不应该用来更新页面的标记设置方法
CustomForm.arr = []
  async fetchMetrics(category = 1, value) {
    while (CustomForm.arr.length > 0) {
        let fn = CustomForm.arr.shift();
        fn();
    }
    let shouldUseResponse = true;
    CustomForm.arr.push(function () {
        shouldUseResponse = false
    })
    
    // 进行了一些异步操作
    await xxx
    
    // 只有最后一次的shouldUseResponse为true,这次对应的结果也正是我们想要的,因此将正确的数据设置上去即可
    shouldUseResponse && this.setState({ metrics });

对于构成闭包的函数,其定义时所处的作用域和执行时所处的作用域不同