javascript - 将 onerror 函数添加到 XMLHttpRequest

标签 javascript

下面是使用 JavaScript 中的 HTTP 请求预加载文件并将其保存到浏览器缓存的代码。问题是,如果没有您的帮助,我无法找到跟踪 preloadMe 函数中的错误的解决方案:)

我知道我们可以将 onerror 添加到 preloadOne 并且它工作正常:

xhr.onerror = function() {
    console.log('onerror happend');
};

但我想知道我们如何在 preloadMe 函数中做到这一点,例如 oncompleteonfetchedonprogress 。像这样的东西:

preload.onerror = items => {
   console.log('onerror happend'); // onerror here
}

非常感谢任何评论或修改...

这是迄今为止我的代码:

(function(global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.Preload = factory());
}(this, (function() {
  'use strict';

  function preloadOne(url, done) {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';
    xhr.onprogress = event => {
      if (!event.lengthComputable) return false
      let item = this.getItemByUrl(event.target.responseURL);
      item.completion = parseInt((event.loaded / event.total) * 100);
      item.downloaded = event.loaded;
      item.total = event.total;
      this.updateProgressBar(item);
    };
    xhr.onload = event => {
      let type = event.target.response.type;
      let blob = new Blob([event.target.response], {
        type: type
      });
      let url = URL.createObjectURL(blob);
      let responseURL = event.target.responseURL;
      let item = this.getItemByUrl(responseURL);
      item.blobUrl = url;
      item.fileName = responseURL.substring(responseURL.lastIndexOf('/') + 1);
      item.type = type;
      item.size = blob.size;
      done(item);
    };
    xhr.send();
  }

  function updateProgressBar(item) {
    var sumCompletion = 0;
    var maxCompletion = this.status.length * 100;

    for (var itemStatus of this.status) {
      if (itemStatus.completion) {
        sumCompletion += itemStatus.completion;
      }
    }
    var totalCompletion = parseInt((sumCompletion / maxCompletion) * 100);

    if (!isNaN(totalCompletion)) {
      this.onprogress({
        progress: totalCompletion,
        item: item
      });
    }
  }

  function getItemByUrl(rawUrl) {
    for (var item of this.status) {
      if (item.url == rawUrl) return item
    }
  }

  function fetch(list) {
    return new Promise((resolve, reject) => {
      this.loaded = list.length;
      for (let item of list) {
        this.onerror(item); // onerror here
        this.status.push({
          url: item
        });
        this.preloadOne(item, item => {
          this.onfetched(item);
          this.loaded--;
          if (this.loaded == 0) {
            this.oncomplete(this.status);
            resolve(this.status);
          }
        });
      }
    });
  }

  function Preload() {
    return {
      status: [],
      loaded: false,
      onprogress: () => {},
      oncomplete: () => {},
      onfetched: () => {},
      onerror: () => {}, // onerror here
      fetch,
      updateProgressBar,
      preloadOne,
      getItemByUrl
    }
  }

  return Preload;

})));

//preload here
preloadMe();

function preloadMe() {

  const preload = Preload();

  preload.fetch([
    'https://round-arm-authority.000webhostapp.com/Ultimate%20Video%20Hack/videos/vid1.mp4'

  ]).then(items => {
    // use either a promise or 'oncomplete'
    console.log(items);
  });

  preload.oncomplete = items => {
    console.log(items);
  }

  preload.onerror = items => {
    console.log('onerror happend'); // onerror here
  }

  preload.onprogress = event => {
    console.log(event.progress + '%');
  }

  preload.onfetched = item => {
    console.log(item);
  }

};

最佳答案

有几个选项。您可以将错误回调添加为参数,就像使用 done 回调所做的那样。

如果您想让错误回调可选,您可以这样做:

function preloadOne(url, done, error) {
  const xhr = new XML HttpRequest();
  // ...
  if(typeof error === 'function') xhr.onerror = error;
  // ...
}

在某些方面,这可能更可取,因为您可以在调用 xhr.send() 之前注册错误处理程序。 (虽然我不知道这是否重要,但我不确定事件循环在这方面是如何工作的。)

另一种方法是在 preloadOne 函数末尾返回 xhr,如下所示:

  function preloadOne(url, done) {
    const xhr = new XMLHttpRequest();
    // ...
    xhr.send();
    return xhr;
  }

然后您可以轻松地添加自己的事件处理程序:

var preload = preloadOne('https://my.site.com/path', x=> {
  console.log('done');
});
preload.onerror = function(event) {
  console.log('error');
};

编辑:return xhr 解决方案的事件循环注意事项。

经过一些研究,我相信如果在 xhr.send() 之后添加事件监听器,可能会丢失错误事件。这是基于MDN event loop页面,特别是添加消息部分,其中显示:

In web browsers, messages are added anytime an event occurs and there is an event listener attached to it. If there is no listener, the event is lost.

因此,虽然在 JavaScript 堆栈为空之前消息不会得到处理,但 XHR 错误的消息排队几乎随时都可能发生。或者如果尚未附加处理程序,则尝试排队。

但是,如果您确实想返回xhr并确保附加事件处理程序的机会,我相信有一种方法可以做到这一点,通过更改preoloadOnexhr.send 放入 setTimeout 中。

setTimeout(()=> xhr.send(), 0);

这会将 send 扔到事件队列中,这意味着它必须等待 JavaScript 调用堆栈清空,然后进入事件循环的下一个周期。当调用堆栈清空时,应附加所有错误事件处理程序。

关于javascript - 将 onerror 函数添加到 XMLHttpRequest,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60174852/

相关文章:

javascript - 标签之间的 CSS 样式空间

javascript - Jest 模拟内部函数

javascript - 使用JavaScript动态更改img标签中的显示样式

javascript - 删除带有本地数据的 jqGrid 行会导致单元格不可单击

javascript - Vue.js/Vuex : How do I v-bind to a state value?

javascript - 在我的网站上显示来自远程服务器的 RSS 源?

javascript - 将二维数组从 javascript 传递到 php 但大小减小

javascript - jQuery 更新图像 srcs 不工作

javascript - 如何创建使用 CSS 或 javascript 制作的 Pert 图表

javascript - 如何通过javascript检查浏览器的在线/离线状态?