【问题标题】:How to resolve race conditions on debounced user input?如何解决去抖动用户输入的竞争条件?
【发布时间】:2016-05-08 21:56:25
【问题描述】:

例如,假设页面返回基于去抖用户文本的搜索结果。

您如何考虑端点具有高度可变延迟的情况,在这种情况下,第二次调用可以在第一次调用之前返回。

例如

用户正在键入 “书籍和电影”,按键去抖时间为 500 毫秒

用户在中间稍微停顿了一下,所以字符串是“books”,这会触发搜索调用。

用户继续输入并完成,触发第二次调用“books and movies”。

第二个调用首先返回,根据“书籍和电影”填充列表。

然后第一个被延迟的调用返回并根据“books”重新呈现列表。

用户只看到“书”,感到困惑。

解决此问题的可靠方法是使用按钮手动触发呼叫。不过我想避免这种情况,所以我增加了去抖动,但我想知道是否有更好的方法。

【问题讨论】:

  • 使用同步 ajax 吗? :)
  • 您应该只在 debouce 过程结束时进行调用。您可以以同步的方式实现这一目标。
  • 不要使用同步 ajax。一种选择是用启动时间标记每个搜索请求,并将该信息包含在响应中。只有当响应比之前的响应“更新”时,您才能填充列表。
  • 时间戳方法将是我的备份选项,但我想知道这是否是一个“已解决”的问题,因为 keyup 上的 ajax 调用是多么常见。也许端点的脆弱性会阻止这种类型的用户体验。

标签: javascript


【解决方案1】:

我们假设您使用 jQuery 进行 ajax 调用。

一种解决方案是使用池系统:基本上是一个包含 ajax 请求的数组。 每次发出新请求时,您都会中止池中的所有请求。 因此,您要确保发出的最后一个请求将是唯一会结束的请求。

这里是池的实现:

jQuery.xhrPool = [];
jQuery.xhrPool.abortAll = function () {
    jQuery(this).each(function (idx, jqXHR) {
        jqXHR.abort();
    });
    jQuery.xhrPool.length = 0;
};

这是一个如何将其与 GitHub (https://developer.github.com/v3/search/#search-repositories) 中的“搜索存储库 API”一起使用的示例:

jQuery.xhrPool = [];
jQuery.xhrPool.abortAll = function () {
  jQuery(this).each(function (idx, jqXHR) {
    jqXHR.abort();
  });
  jQuery.xhrPool.length = 0;
};


$(document).ready(function(){
  $("#SearchField").autocomplete({
    source: function( request, response ) {
        // First we abort all other request
        jQuery.xhrPool.abortAll();
      
        $.ajax({
          url: "https://api.github.com/search/repositories",
          method: "get",
          dataType: "jsonp",
          data: {
            q: request.term
          },
          beforeSend: function (jqXHR) {
            // Before sending the request we add it to the pool. 
            jQuery.xhrPool.push(jqXHR);
          },
          success: function(data) {
            var items = new Array();
            for(var i=0;i<data.data.items.length;i++)
            {
              items.push(data.data.items[i].name);
            }
            response(items);
          }
        });
      },
      minLength: 3,
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.css" >
<input type="text" id="SearchField" />

【讨论】:

    【解决方案2】:

    Javascript 同步工作,因此如果您正确编写代码,则不会出现竞争条件。

    我猜你正在使用 ajax(ajax 应该是异步的,永远不要使用同步,一旦同步,就无法返回)来获取查询结果。您可能正在使用这样的代码:

    var req=new XMLHttpRequest();
    
    
    req.onreadystatechange=function(){
        if (req.readyState==4){
            if (req.status==200){
                // Your callback here which shows autocomplete suggestions maybe?
            }
        }
    }
    

    坚持req 变量。因此,一旦您发出新请求,您可以简单地丢弃旧请求,例如:

    req.onreadystatechange=null;
    

    您也可以abort ajax 请求如:

    req.abort();
    

    【讨论】:

      猜你喜欢
      • 2018-01-22
      • 1970-01-01
      • 2013-10-11
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-19
      相关资源
      最近更新 更多