仅在xhr.onloadend之前已触发时才解决诺言

问题描述 投票:2回答:1

我想在videoLoaded完全加载后立即将true分配给myVideo.mp4。我可以在代码的最后几行执行此操作(这是我们的承诺):

preload.fetch([
      clipSource
]).then(items => {
    // Using a promise it'll fire when we are sure that video clip has finished loading completely
    videoLoaded = true;
}); 

第一个问题是,如果我们的URL无效,则会得到404 response status code。 404本身是有效的响应,因此我们不会触发xhr.onerror(),因为从技术上讲这不是错误。

我们可以使用以下方式跟踪404状态:

xhr.onloadend = function() {
    if(xhr.status == 404) { // do something }       
}

问题是仅在承诺onloadend之后才触发.then(items => { ....事件,因此,如果没有有效的URL,我们将无法阻止承诺解决,即使没有有效的URL,也会将videoLoaded分配给true网址...

我想履行承诺,并且仅在这种情况下videoLoaded可以确定我们具有有效的URL时,才将true分配给xhr.status !== 404

这里是代码(我使用过setInterval,它可以工作,但我认为可以共享更干净的解决方案:]

let onLoadPassed = false;
let videoLoaded = false; 
let clipSource = 'https://mysite/myVideo.mp4';

preload();

// Make sure the video clip is fully loaded
function preload(){

(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.onerror = event => {
        console.log('Error has happend so we restart the video preloading..');
        preload();
    };
    
    xhr.onloadend = function() {
    if(xhr.status == 404){ 
        console.log('404 not found');
        onLoadPassed = false;
        
    } else {
        console.log('File exist');
        onLoadPassed = true;
    }
    
    }
        
    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.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: () => {},
      fetch,
      updateProgressBar,
      preloadOne,
      getItemByUrl
    }
  }

  return Preload;

})));

    const preload = Preload();

    preload.fetch([
      clipSource

    ]).then(items => {
    // Fired when we are sure that video clip has finished loading completely
  
    let check = setInterval(passedFunc, 50);

    function passedFunc() {
   
    if(onLoadPassed === true){
       videoLoaded = true;
       clearInterval(check);
       console.log('videoLoaded: ' + videoLoaded);
    };

  }
    
    });   	
  
};
javascript
1个回答
1
投票

如果状态码为404,您可以截取诺言并抛出错误,这样,后续的.then语句将被忽略,结果将由.catch语句捕获。

preload.fetch([
  clipSource
])
.then(response => {
  if(response.status === 404) 
    throw new Error(response.statusText); //better to use if(!response.ok) as it will capture all failure codes
  else return response;
})
.then(items => {
// Using a promise it'll fire when we are sure that video clip has finished loading completely
  videoLoaded = true;
})
.catch(error => {
  //do something
  console.log(error)
});

推荐问答