haoxuan
2023-08-31 401524fb5661d57ffb2229d683fe4de85b65fd1c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* eslint-disable */
class XhrLoader {
 
  constructor(config) {
    if (config && config.xhrSetup) {
      this.xhrSetup = config.xhrSetup;
    }
  }
 
  destroy() {
    this.abort();
    this.loader = null;
  }
 
  abort() {
    var loader = this.loader;
    if (loader && loader.readyState !== 4) {
      this.stats.aborted = true;
      loader.abort();
    }
 
    window.clearTimeout(this.requestTimeout);
    this.requestTimeout = null;
    window.clearTimeout(this.retryTimeout);
    this.retryTimeout = null;
  }
 
  loadHead(context, config, callbacks) {
    this.context = context;
    this.config = config;
    this.callbacks = callbacks;
    this.stats = {trequest: performance.now(), retry: 0};
    this.retryDelay = config.retryDelay;
    var xhr = new XMLHttpRequest;
    xhr.open('head', context.url);
    xhr.onload = function () {
          callbacks.onSuccess(xhr.getResponseHeader('content-length'));
    };
    xhr.send();
  }
 
  load(context, config, callbacks) {
    this.context = context;
    this.config = config;
    this.callbacks = callbacks;
    this.stats = {trequest: performance.now(), retry: 0};
    this.retryDelay = config.retryDelay;
    this.loadInternal(); 
  }
 
  loadInternal() {
    var xhr, context = this.context;
    if (typeof XDomainRequest !== 'undefined') {
       xhr = this.loader = new XDomainRequest();
    } else {
       xhr = this.loader = new XMLHttpRequest();
    }
    xhr.onloadend = this.loadend.bind(this);
    xhr.onprogress = this.loadprogress.bind(this);
    xhr.open('GET', context.url, true);
    if (context.rangeEnd) {
      xhr.setRequestHeader('Range','bytes=' + context.rangeStart + '-' + (context.rangeEnd-1));
    }
    xhr.responseType = context.responseType;
    let stats = this.stats;
    stats.tfirst = 0;
    stats.loaded = 0;
    if (this.xhrSetup) {
      this.xhrSetup(xhr, context.url);
    }     
    // setup timeout before we perform request
    this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout);
    xhr.send();
  }
 
  loadend(event) {
    var xhr = event.currentTarget,
        status = xhr.status,
        stats = this.stats,
        context = this.context,
        config = this.config;
    // don't proceed if xhr has been aborted
    if (stats.aborted) {
      return;
    }
    // in any case clear the current xhrs timeout
    window.clearTimeout(this.requestTimeout);
 
    // http status between 200 to 299 are all successful
    if (status >= 200 && status < 300)  {
      stats.tload = Math.max(stats.tfirst,performance.now());
      let data,len;
      if (context.responseType === 'arraybuffer') {
        data = xhr.response;
        len = data.byteLength;
      } else {
        data = xhr.responseText;
        len = data.length;
      }
      stats.loaded = stats.total = len;
      let response = { url : xhr.responseURL, data : data };
      this.callbacks.onSuccess(response, stats, context);
    } else {
      // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
      if (stats.retry >= config.maxRetry || (status >= 400 && status < 499)) {
      //  logger.error(`${status} while loading ${context.url}` );
        this.callbacks.onError({ code : status, text : xhr.statusText}, context);
      } else {
      // retry
      //  logger.warn(`${status} while loading ${context.url}, retrying in ${this.retryDelay}...`);
        // aborts and resets internal state
        this.destroy();
        // schedule retry
        this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay);
        // set exponential backoff
        this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay);
        stats.retry++;
      }
    }
  }
 
  loadtimeout() {
  //  logger.warn(`timeout while loading ${this.context.url}` );
    this.callbacks.onTimeout(this.stats, this.context);
  }
 
  loadprogress(event) {
    var stats = this.stats;
    if (stats.tfirst === 0) {
      stats.tfirst = Math.max(performance.now(), stats.trequest);
    }
    stats.loaded = event.loaded;
    if (event.lengthComputable) {
      stats.total = event.total;
    }
    let onProgress = this.callbacks.onProgress;
    if (onProgress) {
      // last args is to provide on progress data
      onProgress(stats, this.context, null);
    }
  }
}
 
export default XhrLoader;