【问题标题】:Javascript: Reduce timeout value for asynchronous xhr file upload after file has been completely sent?Javascript:在文件完全发送后减少异步 xhr 文件上传的超时值?
【发布时间】:2018-08-04 10:06:37
【问题描述】:

通常我总能找到解决我在 stackoverflow 上的问题的方法,你们真的很棒! 但是经过数小时的搜索后,我仍然遇到了这个问题:

我正在为 Chrome/FF 编写浏览器扩展程序,它通过 xhr post 方法将文件上传到服务器。如果服务器没有响应,我想给用户一个连接超时的反馈。因此,首先我使用了 10000 毫秒的超时值,因为我认为超时仅在请求/文件完全发送后才开始计数。然而,事实证明,在慢速连接上,上传时间超过 10 秒,并且在文件完全发送之前触发了超时事件。对于一些较大的文件,上传甚至需要几分钟,因此我不得不将超时值设置为 5 分钟。然而,这在快速连接上是不切实际的。在快速连接上,如果服务器没有响应,我不想在通知用户连接超时之前等待 5 分钟。

因此我正在寻找一个 xhr 解决方案,它应该:

  1. 首先将超时设置为例如在文件上传期间 5 分钟,以便在慢速连接时也能完全发送文件
  2. 将超时设置为例如文件完全发送后 10 秒,如果服务器在收到整个文件后 10 秒内没有响应,则连接超时。

有没有可能做到这一点?

编辑:到目前为止我尝试的是:

[...]
var originalTimeout = 10000;
xhr.timeout = originalTimeout;
[...]

xhr.upload.addEventListener('loadstart', function(e) {
    xhr.timeout = 600000;
});
xhr.upload.addEventListener('loadend', function(e) {
    xhr.timeout = originalTimeout;
});

我进行了几次上传测试,显然超时值被上传-'loadstart' 事件监听器更改为 600000,因为发送文件通常需要 10 秒以上,并且没有触发超时事件。但此后总是在文件完全发送到服务器后立即触发超时事件。我能够在服务器端确认文件已完全接收并且响应已发送。但是,由于之前触发了超时事件,因此脚本没有收到响应。

任何想法都会非常感激!

【问题讨论】:

    标签: javascript asynchronous file-upload xmlhttprequest timeout


    【解决方案1】:

    您可以使用Promise,特别是Promise.race 方法,以及XMLHttpRequestUpload 对象。您可以让一个承诺成为计时器,而另一个承诺等待XMLHttpRequest

    var xhr = new XMLHttpRequest();
    xhr.timeout = (1000 * 60) * 5 //5 minute initial timeout timer.
    
    xhr.ontimeout = function() {
      doSomething(); //if 5 minute timeout is done.
    };
    
    var xhrUpload = xhr.upload;
    xhrUpload.loadend = function() { //when upload is done, move on to 10s server timer.
    raceThis(xhr, callback)
    }
    
    xhr.open('METHOD', url, true);
    
    function raceThis(xmlReq, callback) {
      var waitForServer = new Promise(function(resolve, reject) {
        xmlReq.onreadystatechange = function() {
          if(xmlReq.readyState = 2) { 
            resolve(false); //the moment HTTP headers are sent as a response, resolve
          }
        }
      })
      var waitFor10Seconds = new Promise(function(resolve, reject) {
        window.setTimeOut(function() {
          resolve(true) 
        }, 10000)
      })
    
      Promise.race([waitForServer(), waitFor10Seconds()])
      .then(function(serverTimedOut) {
        if(serverTimedOut) {
          //do stuff if the server timed out 
        } else {
          //do stuff if the server didnt time out
        };
    
        callback(serverTimedOut) //you can even use dedicated callbacks! 
      })
    }
    

    这是一个粗略的表示,有很多方法可以对此进行扩展,但这是主要思想。

    【讨论】:

    • 非常感谢这个想法。只是为了我的理解:在代码中应该说“Promise.race”而不是“Promise.all”,不是吗?到目前为止,我还没有使用过多的 Promise...
    【解决方案2】:

    亲爱的,你只能在请求发送之前设置超时值。一旦发送请求,您将无法设置它。我鼓励您避免为此类请求设置超时,因为您无法保证用户将上传的文件大小。这是一种不好的做法。

    很多事情会使请求花费更长的时间。例如由于内存不足等其他原因导致浏览器挂起,另一个是网络连接速度慢,另一个是由于脚本运行时间过长,服务器响应时间过长。对于具有快速互联网的设备,您的回调函数将执行得更快,您无法控制它,用户喜欢他的快速互联网,仅此而已。对于其他连接速度较慢的人,他们知道连接速度太慢。

    您也可以通过监听进度事件并显示进度条来监控进度,这将增强用户体验并使其生动。

    我不建议您在一段时间后中止请求,因为这是一个状态更改操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-27
      • 2023-03-05
      • 2016-07-28
      • 1970-01-01
      • 2018-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多