【问题标题】:Which is the "right" way to handle a response that came late这是处理迟到的响应的“正确”方式
【发布时间】:2015-05-04 18:35:32
【问题描述】:

假设我们有两个按钮,每个按钮都调用以下方法:

var NUMBER_OF_IMAGE_REQUEST_RETRIES = 3;
var IMAGE_REQUEST_TIMEOUT = 3000;


processImage: function(image_data) {
    var main_response = $q.defer();
    var hash = getImageHash(image_data);

    var requestsCounter = -1;

    requestImage = function() {
      $http.post(apiUrl, {params: {data: hash},timeout: IMAGE_REQUEST_TIMEOUT})
       .then(function(response) {
           return main_response.resolve(response.data);
        }, function(error) {
        if (++requestsCounter < NUMBER_OF_IMAGE_REQUEST_RETRIES) {
          requestLabelsImage();
        } else {
          return main_response.reject();
        }
      });
    };

    requestLabelsImage();

    return main_response.promise;
}

该方法将图像相关数据传递给服务器,服务器处理数据然后响应。每次用户按下不同的按钮时,都会向服务器发送不同的image_data

问题:

用户按下按钮1,方法被image_data_1调用,然后他/她立即按下按钮2,方法被image_data_2调用。 processImage 函数由另一种方法调用,假设 doSomethingWithTheResponse 只关心最新用户的操作,但 image_data_2 由服务器执行得更快,因此客户端在 image_data_1 之前获得 image_data_2,所以客户认为image_data_1与用户最近的操作有关,事实并非如此。 我们如何确保客户端始终获得与用户最新操作相关的响应?

注意:不同image_data 请求的哈希值不同。

我在想这样的事情:

var oldhash = null;

processImage: function(image_data) {
    var main_response = $q.defer();
    var hash = getImageHash(image_data);
    oldhash = hash;

    var requestsCounter = -1;

    requestImage = function(hash) {
     if(hash === oldhash){

      $http.post(apiUrl, {params: {data: hash},timeout: IMAGE_REQUEST_TIMEOUT})
       .then(function(response) {
           return main_response.resolve(response.data);
        }, function(error) {
        if (++requestsCounter < NUMBER_OF_IMAGE_REQUEST_RETRIES) {
          requestLabelsImage(hash);
        } else {
          return main_response.reject();
        }
      });

    }
    else {
      main_response.reject();
    }
   }

    requestLabelsImage(hash);

    return main_response.promise;
}

但我不能 100% 确定这是正确的方法。

【问题讨论】:

  • 你不能在按下第二个按钮时取消第一个请求吗? developer.mozilla.org/en-US/docs/Web/API/…
  • “不要让你的用户做他们应该做的事情”。为什么不让按钮 2 处于禁用状态,直到按钮 1 的响应返回并得到处理?
  • @Mike'Pomax'Kamermans 因为根据 UI/UX 要求,用户应该能够以任意顺序单击按钮(并向服务器发送请求),但客户端应该呈现他最近行动的结果。
  • 你也可以过滤。 if (image_data_1) { /* do this */} if (image_data_2) { /* do that */}.

标签: javascript angularjs asynchronous


【解决方案1】:

忽略之前的请求。

您可以创建请求的存储库(数组或字典实现都可以)。一旦提出另一个请求,请在之前的请求上调用 .abort() - 当您将其添加到存储中时。

如果你想要一本字典,有一个很好的例子 here(虽然涉及不同的主题),但这里是他的代码的修改后的 sn-p,它与你的案例相关:

var _pendingRequests = {};

function abortPendingRequests(key) {
    if (_pendingRequests[key]) {
        _pendingRequests[key].abort();
    }
}

key 可以在哪里......比如说......你的行为的一个类别。您可以为其命名常量,也可以只是按下按钮的名称。它甚至可以是您请求的 URL;完全取决于你。

这里对整个概念有很好的解释:

jquery abort() ajax 请求在发送另一个之前 https://stackoverflow.com/a/3313022/594992

【讨论】:

  • 我使用你的想法实现了它,这就是我支持它的原因,有一个 repositoryOfRequests,键是 has 和 value the promise,每次新请求到达时都会取消承诺。吨。
  • 我希望它对你有用。请不要忘记在正确答案上打勾。干杯!
【解决方案2】:

如果您的 UI 允许启动多个操作,而处理这些操作是互斥的,那么您可能应该使用 Promise,并跟踪活动的 Promise。

button1.addEventListener("click", function(evt) {
  startRunning( task1.start() );
});

button2.addEventListener("click", function(evt) {
  startRunning( task2.start() );
});

使用如下任务运行器:

function startRunning( promise ) {
  while(runningTasks.length>0) {
    cancel( runningTasks.unshift() );
  });
  runningTasks.push( promise );
}

您的cancel 函数可以来自任何可以处理promise 的东西,例如Angular 的service.cancelRequest,或者您可以编写自己的代码来接受promise 并巧妙地中断它的操作。

当然,如果您不使用 Promise,那么您可能想要开始这样做,但如果您绝对不能,您可以使用如下管理器对象:

button1.addEventListener("click", function(evt) { task1(); });

button2.addEventListener("click", function(evt) { task2(); });

var manager = [];

function cancelAll() {
  while(manager.lenght>0) {
    var cancelfn = manager.unshift()
    cancelfn();
  }
  return true;
}

function task1() {
  var running = cancelAll();
  manager.push(function() { running = false; });
  asyncDo(something1, function(result) {
    if(!running) return;
    // do your real thing
  });
}

function task1() {
  var running = cancelAll();
  manager.push(function() { running = false; });
  asyncDo(something2, function(result) {
    if(!running) return;
    // do your real thing
  });
}

您可以根据需要在多个方面取消。如果您需要取消正在运行的 XHR,如果您在结果处理中有多个步骤、在每个步骤开始时中断等,您也许可以这样做。

【讨论】:

  • 我赞成你的问题,即使这不是我所需要的,因为我从中学到了;)谢谢迈克
  • 这是最好的投票方式,如果有用就给分,即使它不是最终答案,还有一个很好的“正确答案”的大勾号=)
【解决方案3】:

这听起来像是 Promise 的理想用例。基本上,每当提出新请求时,您都想取消任何现有的承诺。我不精通 AngularJS,但以下特定于 ng 的链接可能很有用:

Angularjs how to cancel resource promise when switching routes

Canceling A Promise In AngularJS

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-19
    • 2015-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 1970-01-01
    相关资源
    最近更新 更多