Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

多线程下载技术尝试

  在网络状态不好的情况下(掉包率高的情况),连接会变得非常不稳定,一个连接需要持续的时间越长就越有可能陷入TCP的等待机制中。这种情况下如果缩短单个连接的持续时间就可以提高加载速度,所以我们可以尝试把稍大的文件切开,用多个HTTP请求同时下载。
  通常情况下,加载一张图片会有类似这样的代码 <script>
onload=function(){
  var test="直接加载整个文件";
  console.time(test);
  var url="http://www.web-tinker.com/share/lena_std.png";
  var img=new Image;
  img.src=url;
  img.onload=function(){
    document.body.appendChild(img);
    console.timeEnd(test);
  };
};
</script>
  但由于这个代码中加载的图片比较大,如果网络不太通畅的话加载会经常出现卡在一半的情况。这时候可能需要手动重新发起请求,这也是为什么我们有时遇到页面加载卡住时会刷新页面的原因。

  下面是我稍做封装的多线程下载方案,这里偷了个懒,直接用了一些ES6的特性,没做低版本浏览器兼容 <script>
var loadFile=(function(){
  const MAX_CONNECTIONS=4; //连接数限制
  const BLOCK_SIZE=10*1024; //单个数据块大小(10KB)
  const TIMEOUT=10*1000; //请求超时(10秒)
  const RETRY=5; //重试次数(5次)
  var request=(function(){
    var queue=[],count=0;
    return function(url,begin,end){
      var retry=RETRY;
      return new Promise(function callee(resolve,reject){
        if(count>=MAX_CONNECTIONS)
          return queue.push(callee.bind(this,resolve,reject));
        count++;
        var xhr=new XMLHttpRequest;
        xhr.open("GET",url,true);
        xhr.responseType="arraybuffer";
        xhr.setRequestHeader("Range","bytes="+begin+"-"+end);
        var itv=setTimeout(function(){
          xhr.onerror=null;
          xhr.abort();
          count--;
          if(retry--)callee(resolve,reject);
          else reject();
        },TIMEOUT);
        xhr.onload=function(){
          clearTimeout(itv);
          resolve(new Uint8Array(xhr.response));
          count--;
          if(queue.length)queue.shift()();
        };
        xhr.onerror=reject;
        xhr.send();
      });
    };
  })();
  return function(url){
    var mime;
    return new Promise(function(resolve,reject){
      var xhr=new XMLHttpRequest;
      xhr.open("HEAD",url,true);
      xhr.onload=function(){
        mime=xhr.getResponseHeader("Content-Type");
        resolve(+xhr.getResponseHeader("Content-Length"));
      };
      xhr.onerror=reject;
      xhr.send();
    }).then(function(length){
      return Promise.all(Array.prototype.slice.call(
        new Uint8Array(Math.ceil(length/BLOCK_SIZE)),0
      ).map(function(e,i){
        i*=BLOCK_SIZE;
        return request(url,i,Math.min(length,i+BLOCK_SIZE-1));
      }));
    }).then(function(s){
      return Promise.resolve(new Blob(s,{type:mime}));
    });
  };
})();

onload=function(){
  var test="多线程分块下载,并使用超时重连";
  console.time(test);
  var url="http://www.web-tinker.com/share/lena_std.png";
  loadFile(url).then(function(blob){
    var img=new Image;
    img.src=URL.createObjectURL(blob);
    document.body.appendChild(img);
    console.timeEnd(test);
  });
};
</script>

  当然,多线程下载也并不是总比正常模式的下载快,只有在网络情况差时才能看出差异。正常情况下他们的性能是差不多的,甚至在网络非常时直接下载整个文件会更快。而且多线程下载对服务器的资源占用也比较大,所以并不是传统下载方式的替代方案。
  我觉得还需要另外引入一个网络掉包率和带宽的检测来选择合适的加载方式,对多线程下载方案也可以通过这些检测数据来配置相关参数,以获得最佳下载效果。当然,这些东西并不简单,还需要一些研究才能得出可用性结论。
网名:
3.80.32.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^