nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

index.js (5653B)


      1 import {arrayBufferToString} from '~/lib/binary-strings';
      2 
      3 function successIfStatus200(xhr) {
      4     return xhr.status >= 200 && xhr.status <= 299;
      5 }
      6 
      7 function retryIfNetworkErrorOrServerError(xhr) {
      8     return (xhr.status === 0 || xhr.status >= 500);
      9 }
     10 
     11 class XMLHttpRequestPromiseError extends Error {
     12     constructor(xhr) {
     13         super();
     14         this.xhr = xhr;
     15         this.name = 'XMLHttpRequestPromiseError';
     16 
     17         this.message = xhr.status === 0 ? 'network error' : `server response is ${xhr.status}`;
     18     }
     19 }
     20 
     21 class XMLHttpRequestPromise {
     22     constructor(
     23         url, {method = 'GET', data = null, responseType = '', timeout = 30000, maxTries = 3, retryTimeWait = 500,
     24             isResponseSuccess = successIfStatus200, responseNeedsRetry = retryIfNetworkErrorOrServerError,
     25             headers = null, withCredentials = false} = {}) {
     26         // console.log('promise constructor', url);
     27         const promise = new Promise((resolve, reject) => {
     28                 this._resolve = resolve;
     29                 this._reject = reject;
     30             }
     31         );
     32         this.then = promise.then.bind(promise);
     33         this.catch = promise.catch.bind(promise);
     34         this.method = method;
     35         this.url = url;
     36         this.responseType = responseType;
     37         this.postData = data;
     38         this._isResponseSuccess = isResponseSuccess;
     39         this._responseNeedsRetry = responseNeedsRetry;
     40         this._retryTimeWait = retryTimeWait;
     41         this.triesLeft = maxTries;
     42 
     43         const xhr = this.xhr = new XMLHttpRequest();
     44         xhr.onreadystatechange = () => this._onreadystatechange();
     45         this._open();
     46         xhr.timeout = timeout;
     47         if (responseType === 'binarystring') {
     48             xhr.responseType = 'arraybuffer';
     49         } else {
     50             xhr.responseType = responseType;
     51         }
     52         if (headers) {
     53             for (let [k, v] of headers) {
     54                 xhr.setRequestHeader(k, v);
     55             }
     56         }
     57         xhr.withCredentials = withCredentials;
     58     }
     59 
     60     _open() {
     61         // console.log('open', this.url);
     62         this.xhr.open(this.method, this.url);
     63     }
     64 
     65     _onreadystatechange() {
     66         const xhr = this.xhr;
     67         if (xhr.readyState === 4 && !this._aborted) {
     68             // console.log('ready state 4', this.url);
     69             xhr.responseBinaryText = '';
     70             if (this.responseType === 'binarystring' && xhr.response && xhr.response.byteLength) {
     71                 xhr.responseBinaryText = arrayBufferToString(xhr.response);
     72             }
     73             // IE doesnot support responseType=json
     74             if (this.responseType === 'json' && (typeof xhr.response) === 'string') {
     75                 try {
     76                     // xhr.response is readonly
     77                     xhr.responseJSON = JSON.parse(xhr.response);
     78                 } catch (e) {
     79                     xhr.responseJSON = null;
     80                 }
     81             } else {
     82                 xhr.responseJSON = xhr.response;
     83             }
     84             if (this._isResponseSuccess(xhr)) {
     85                 // console.log('success', this.url);
     86                 this._resolve(xhr);
     87             } else {
     88                 if (this.triesLeft > 0 && this._responseNeedsRetry(xhr)) {
     89                     // console.log('retry', this.url);
     90                     this._open();
     91                     this._timerId = setTimeout(() => this.send(), this._retryTimeWait);
     92                 } else {
     93                     // console.log('failed', this.url);
     94                     this._reject(new XMLHttpRequestPromiseError(xhr));
     95                 }
     96             }
     97         }
     98     }
     99 
    100     abort() {
    101         // console.log('abort', this.url);
    102         this._aborted = true;
    103         clearTimeout(this._timerId);
    104         this.xhr.abort();
    105     }
    106 
    107     send() {
    108         // console.log('send', this.url);
    109         this.triesLeft -= 1;
    110         this.xhr.send(this.postData);
    111     }
    112 }
    113 
    114 class XHRQueue {
    115     constructor(maxSimultaneousRequests = 6) {
    116         this._maxConnections = maxSimultaneousRequests;
    117         this._queue = [];
    118         this._activeCount = 0;
    119     }
    120 
    121     put(url, options) {
    122         const promise = new XMLHttpRequestPromise(url, options);
    123         promise._originalAbort = promise.abort;
    124         promise.abort = () => this._abortPromise(promise);
    125         this._queue.push(promise);
    126         this._processQueue();
    127         return promise;
    128     }
    129 
    130     _abortPromise(promise) {
    131         const i = this._queue.indexOf(promise);
    132         if (i > -1) {
    133             // console.log('ABORT IN QUEUE');
    134             this._queue.splice(i, 1);
    135         } else {
    136             if (promise.xhr.readyState === 4) {
    137                 // console.log('ABORT COMPLETED');
    138             } else {
    139                 // console.log('ABORT ACTIVE');
    140                 promise._originalAbort();
    141                 this._activeCount -= 1;
    142                 setTimeout(() => this._processQueue(), 0);
    143             }
    144         }
    145     }
    146 
    147     _processQueue() {
    148         if (this._activeCount >= this._maxConnections || this._queue.length === 0) {
    149             return;
    150         }
    151         const promise = this._queue.shift();
    152         promise
    153             .catch(() => {
    154                 // do not throw if XHR request fails
    155             })
    156             .then(() => this._onRequestReady(promise));
    157         this._activeCount += 1;
    158         promise.send();
    159     }
    160 
    161     _onRequestReady(promise) {
    162         if (!promise._aborted) {
    163             this._activeCount -= 1;
    164         }
    165         setTimeout(() => this._processQueue(), 0);
    166     }
    167 }
    168 
    169 function fetch(url, options) {
    170     // console.log('fetch', url);
    171     const promise = new XMLHttpRequestPromise(url, options);
    172     promise.send();
    173     return promise;
    174 }
    175 
    176 export {fetch, XHRQueue};
    177