防止先前的但更长的-运行 $http 请求在最近的请求之后返回

Prevent previous, yet longer-running $http request from returning after most recent request

我刚刚遇到一个奇怪的情况,经过一番搜索后无法找到答案。

我有一个文本框,允许用户键入关键字来过滤 table 数据。我在输入元素上有一个 ng-change 指令,它将触发此代码:

return $http.get(url).then(function (response) {
    return response.data;
});

所有这一切都运行良好,直到我们的测试人员刚刚在 IE11 中发现一些不良行为。这是在 IE10 中的文本框中键入 "M-A-T-T" 的示例:

如您所见,每个后续请求比第一个请求花费的时间更长,因此第四个也是最后一个请求的结果是控制器收到的结果。

这是在 IE11 的文本框中键入 "M-A-T-T" 的相同示例。

令人费解的是,第二个请求几乎需要 2 秒才能完成,这是在第四个也是最后一个请求完成之后。这导致 "MA" 请求的结果在用户期望 "MATT" 请求的结果时显示(这是当前在他们的文本框中的内容)。

Angular如何处理这个问题?提前致谢。

更新 根据 frosty 的回应,我实施了以下(除了弹跳之外)效果很好:

var cancelDeferred;

var getUsers = function (criteria) {
    if (cancelDeferred) {
        cancelDeferred.resolve();
    }
    cancelDeferred = $q.defer();
    return $http.get(url, { timeout: cancelDeferred.promise }).then(function (response) {
        cancelDeferred = undefined;
        return response.data;
    });
};

实际上,主要挑战是在调用此方法时处理错误。超时 return 是一个错误,就像 500 return 是一个错误一样。我想忽略超时并处理实际错误。这是我的解决方案:

function onError(data, status) {
    if (data.data !== null && data.status !== 0) {
        //handle error
    }
}

现在我将尝试弄清楚是否有一种方法可以在全局范围内实现此承诺超时,而不必更改大量 $http.get() 调用...

在 $http 的文档中,它讨论了一个配置对象,您可以将其作为第二个参数传递给 $http.get 调用,如下所示:

$http.get(url, config);

在该配置中,它讨论了您可以设置的 "timeout" 属性。如果您将 属性 设置为 Promise,那么如果您 resolve 这个 promise,它将中止请求。查看文档以了解那里的描述。

即使你使用去抖动,你也会想要使用这个 "timeout" 来中止请求。

https://docs.angularjs.org/api/ng/service/$http

所以你会这样做:

var cancelDeferred;
function makeRequest(){
    if(cancelDeferred) cancelDeferred.resolve(); //when this gets resolved, the previous request will abort. 
    cancelDeferred = $q.defer();
    return $http.get(url,{timeout:cancelDeferred.promise})
        .then(function(res){
            cancelDeferred = undefined;
            return res;
        });
}

因此,您将承诺传递给 .get 请求,在配置对象内部作为 "timeout" 属性。如果您在响应返回之前解决此延迟,它将中止请求。