【问题标题】:Kill/terminate ajax request if a new one is fired如果触发了新请求,则终止/终止 ajax 请求
【发布时间】:2017-06-15 18:59:26
【问题描述】:

我已经构建了一个 javascript 应用程序,它有一个带有一些用户可以更改的下拉过滤器的图表。下拉列表都有事件侦听器,它们提交服务器请求以获取数据(通过 jquery ajax 调用),然后绘制数据图表。问题是用户是否使用键盘快速浏览下拉菜单的许多不同元素。

服务器调用大约需要一秒钟,所以如果他们快速滚动到 20,这可能会导致堆积。创建了 20 个对服务器的不同请求,然后甚至不能保证在服务器请求成功时执行的最后一段代码将是最新的过滤器。

所以我的问题是,当过滤器被更改以终止所有其他异步进程时,最好的方法是什么?以下是相关代码:

$("#filter").change(function(d) {
  getData();
} //end of if
});

function getData() {
  ...
  $.ajax({
    url: myUrl
    type: "GET",
    dataType: "json",
    success: function(d) {
        [do some stuff]
      } //end of success function
  }); //end of ajax
} //end of getData

【问题讨论】:

  • 第一个函数中有一个额外的大括号,表示它关闭了if
  • 您应该等到当前运行的请求完成后再发送一个新请求(并忽略当前运行时可能发生的一些请求)。即使您使用XMLHttpRequest2abort 功能,这仍然可能导致不必要的高服务器负载。

标签: javascript jquery ajax events


【解决方案1】:

将您希望中止的所有 Ajax 调用保存到某个变量中,然后您可以中止最后一次调用。

这是一种非常普遍的做法,当一些 ajax 调用可能在有机会完成之前发生多次时。

function getData(){
    $filter.data('REQ') && $filter.data('REQ').abort(); // abort the last request before creating a new one

    return $.ajax({
        url:myUrl
        type:"GET",
        dataType:"json",
        success:function(d){
            [do some stuff]
        }
    })
}

var $filter = $("#filter");

$filter.on('change', function(d) {
    $filter.data('REQ', getData())
});

当然,这是一种非常简化的代码方式,您应该以更结构化的方式重新编写它,但它让您了解如何缓存最后一个 ajax 请求,然后您可以中止它在发送新呼叫之前。


顺便说一句,您的标题与问题无关。您正在询问如何处理一系列 ajax 调用但询问事件。这个问题与事件无关,所以你应该改变标题以适应问题。


更新关于@t.niese 在 cmets 中所说的话:

在客户端限制请求也是一个很好的做法,因为服务器无法真正知道客户端是否中止了请求,如果这是一个资源要求高的请求,则应该对其进行限制。 但是,如果可能的话,我建议在服务器端而不是在客户端限制请求,因为客户端限制可以被绕过并且不是 100% 可靠的,并且它“花费”相同数量的是时候在服务器端执行此操作了。

Can a http server detect that a client has cancelled their request?

【讨论】:

  • 我稍微修改了代码,只是将 ajax 请求存储到具有全局范围的变量中,并在 getData 开始时对该变量调用了 abort,但该方法非常有效。
  • 应该提到abort只会在客户端停止请求,但是一旦它到达服务器,服务器可能仍然需要对请求进行整个处理,即使它是中止。所以它可能不会对服务器负载产生影响,如果这导致瓶颈,那么abort 将无济于事,并且应该限制发出的请求数。 @zachvac
  • @t.niese - 这取决于您调用getData 方法的速度。如果您快速调用它,那么请求将没有足够的时间到达服务器。服务器可以也应该自行检测是否存在相同请求的快速流(每个用户 ID),并且应该在服务器上对其进行限制,因为当人们可以简单地绕过时,客户端限制毫无价值它很容易或通过人为错误。
【解决方案2】:

当当前请求正在运行时,我将放弃所有尝试发起调用getData 的新请求,并且仅在当前请求完成后立即发送最后一次getData 尝试。这将确保服务器负载不会变得不必要的高,因为只会运行一个请求。

var currentRequest;
var resubmitRequest;

function getData() {

  // only start a new request id no current request is running
  if (!currentRequest) {
    resubmitRequest = false;
    currentRequest = Promise.resolve($.ajax({
      url: myUrl
      type: "GET",
      dataType: "json"
    })); //end of ajax

    currentRequest
      .then((d) => {
        [do some stuff]
      })
      .finally(() => {
        // if the current request finished and another attempt to request data
        // happened while this request was running then call getData again
        if (resubmitRequest) {
          getData()
        }
      })

  } else {
    // store the information that the data has to be requested 
    // another time after the currently running request finished
    resubmitRequest = true;
  }

  return currentRequest;
} //end of getData

【讨论】:

    【解决方案3】:

    您可以简单地将其暂停到 5 亿秒,然后运行“最后”更改并执行该 ajax 调用..

    $("#filter").change(function(d) {
      if( $.active > 0 ) return false;
      // do not run new ajax if last one is not finished
      clearTimeout( window.timer ); 
      // if new 'change' evt is raised in less then 500 mils
      // then clear it and run 'last' one
      window.timer = setTimeout( function(){
        getData();
      }, 500 );
    });
    

    如果用户在执行 ajax 时更改它,return false it :)

    【讨论】:

      【解决方案4】:

      您可以为这些情况编写自己的 handler 代码:

      function handler(ms, fn) {
          var eventId;
          return function () {
              // if there is an event programmed, kill it
              clearTimeout(eventId);
              // and program the new one (replace the event)
              eventId = setTimeout(fn);
              // if passed the time no event was programmed, setTimeout is going to execute the function
          }
      }
      
      // execute getData if no event is fired in a 1000ms interval (the user stopped typing)
      // getData is the same that you have
      var fn = handler(1000, getData);
      
      $("#filter").change(fn);   
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-06
        • 2014-06-11
        • 1970-01-01
        相关资源
        最近更新 更多