nakarte

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

commit 167ba25c4f265c57a2846cb7f0045b2c0f9afc8f
parent 06289c3790bd0fdddb4566e69c5fc135c1fe2a93
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Tue, 29 Nov 2016 01:38:13 +0300

xhr-promise -- total rewrote, added retries on errors, abort method

Diffstat:
Msrc/lib/leaflet.layer.geojson-ajax/geojson-ajax.js | 11++++++-----
Msrc/lib/leaflet.layer.westraPasses/westraPasses.js | 6++----
Msrc/lib/leaflet.layer.westraPasses/westraPassesMarkers.js | 13++++++-------
Msrc/lib/xhr-promise/xhr-promise.js | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
4 files changed, 91 insertions(+), 36 deletions(-)

diff --git a/src/lib/leaflet.layer.geojson-ajax/geojson-ajax.js b/src/lib/leaflet.layer.geojson-ajax/geojson-ajax.js @@ -1,5 +1,5 @@ import L from 'leaflet'; -import {XMLHttpRequestPromise} from 'lib/xhr-promise/xhr-promise'; +import {fetch} from 'lib/xhr-promise/xhr-promise'; import {notifyXhrError} from 'lib/notifications/notifications'; L.Layer.GeoJSONAjax = L.GeoJSON.extend({ @@ -17,10 +17,11 @@ L.Layer.GeoJSONAjax = L.GeoJSON.extend({ return; } this._loadStarted = true; - const {promise} = XMLHttpRequestPromise(this.url, - {responseType: 'json', timeout: this.options.requestTimeout}); - promise.then((xhr) => this.addData(xhr.response), - (xhr) => notifyXhrError(xhr, `GeoJSON data from ${this.url}`)) + fetch(this.url, {responseType: 'json', timeout: this.options.requestTimeout}) + .then( + (xhr) => this.addData(xhr.response), + (xhr) => notifyXhrError(xhr, `GeoJSON data from ${this.url}`) + ) }, onAdd: function(map) { diff --git a/src/lib/leaflet.layer.westraPasses/westraPasses.js b/src/lib/leaflet.layer.westraPasses/westraPasses.js @@ -15,14 +15,12 @@ L.Layer.WestraPasses = L.Layer.extend({ this.markers = new westraPasesMarkers(baseUrl); this.regions1 = new L.Layer.GeoJSONAjax(baseUrl + this.options.fileRegions1, { className: 'westra-region-polygon', - onEachFeature: this._setRegionLabel.bind(this, 'regions1'), - requestTimeout: 10000 + onEachFeature: this._setRegionLabel.bind(this, 'regions1') } ); this.regions2 = new L.Layer.GeoJSONAjax(baseUrl + this.options.fileRegions2, { className: 'westra-region-polygon', - onEachFeature: this._setRegionLabel.bind(this, 'regions2'), - requestTimeout: 10000 + onEachFeature: this._setRegionLabel.bind(this, 'regions2') } ); }, diff --git a/src/lib/leaflet.layer.westraPasses/westraPassesMarkers.js b/src/lib/leaflet.layer.westraPasses/westraPassesMarkers.js @@ -4,7 +4,7 @@ import openPopup from 'lib/popupWindow/popupWindow'; import escapeHtml from 'escape-html'; import {saveAs} from 'browser-filesaver'; import iconFromBackgroundImage from 'lib/iconFromBackgroundImage/iconFromBackgroundImage'; -import {XMLHttpRequestPromise} from 'lib/xhr-promise/xhr-promise'; +import {fetch} from 'lib/xhr-promise/xhr-promise'; import {notifyXhrError} from 'lib/notifications/notifications'; @@ -25,12 +25,11 @@ const westraPasesMarkers = L.Layer.CanvasMarkers.extend({ return; } this._downloadStarted = true; - const {promise} = XMLHttpRequestPromise(this.url, - {responseType: 'json', timeout: 30000} - ); - promise.then((xhr) => this._loadMarkers(xhr), - (xhr) => notifyXhrError(xhr, 'westra passes data') - ); + fetch(this.url, {responseType: 'json'}) + .then( + (xhr) => this._loadMarkers(xhr), + (xhr) => notifyXhrError(xhr, 'westra passes data') + ); }, diff --git a/src/lib/xhr-promise/xhr-promise.js b/src/lib/xhr-promise/xhr-promise.js @@ -1,28 +1,86 @@ -function makeRequest(url, {method='GET', data=null, responseType='', timeout=0} = {}) { - const xhr = new XMLHttpRequest(); - xhr.open(method, url); - xhr.timeout = timeout; - if (responseType === 'binarystring') { - xhr.responseType = 'text'; - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - } else { - xhr.responseType = responseType; +function successIfStatus200(xhr) { + return xhr.status >= 200 && xhr.status <= 299; +} + +function retryIfNetworkErrorOrServerError(xhr) { + return (xhr.status === 0 || xhr.status >= 500); +} + +class XMLHttpRequestPromise { + constructor( + url, {method='GET', data=null, responseType='', timeout=30000, maxTries=3, retryTimeWait=1000, + isResponseSuccess=successIfStatus200, responseNeedsRetry=retryIfNetworkErrorOrServerError} = {}) { + // console.log('promise constructor', url); + const promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + } + ); + this.then = promise.then.bind(promise); + this.catch = promise.catch.bind(promise); + this.method = method; + this.url = url; + this._isResponseSuccess = isResponseSuccess; + this._responseNeedsRetry = responseNeedsRetry; + this._retryTimeWait = retryTimeWait; + this.triesLeft = maxTries; + + const xhr = this.xhr = new XMLHttpRequest(); + xhr.onreadystatechange = () => this._onreadystatechange(); + this._open(); + xhr.timeout = timeout; + if (responseType === 'binarystring') { + xhr.responseType = 'text'; + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } else { + xhr.responseType = responseType; + } } - const promise = new Promise(function(resolve, reject) { - xhr.onreadystatechange = function() { - if (xhr.readyState === 4) { - resolve(xhr); + _open() { + // console.log('open', this.url); + this.xhr.open(this.method, this.url); + } + + _onreadystatechange() { + const xhr = this.xhr; + if (xhr.readyState === 4 && !this._aborted) { + // console.log('ready state 4', this.url); + if (this._isResponseSuccess(xhr)) { + // console.log('success', this.url); + this._resolve(xhr); + } else { + if (this.triesLeft > 0 && this._responseNeedsRetry(xhr)) { + // console.log('retry', this.url); + this._open(); + this._timerId = setTimeout(() => this.send(), this._retryTimeWait); + } else { + // console.log('failed', this.url); + this._reject(xhr); + } } } - }); - return {xhr, promise, send: xhr.send.bind(xhr)}; + } + + abort() { + // console.log('abort', this.url); + this._aborted = true; + clearTimeout(this._timerId); + this.xhr.abort(); + } + + send() { + // console.log('send', this.url); + this.triesLeft -= 1; + this.xhr.send(); + } } -function XMLHttpRequestPromise(url, {method='GET', data=null, responseType='', timeout=0} = {}) { - const {xhr, promise} = makeRequest(url, {method, data, responseType, timeout}); - xhr.send(); +function fetch(url, options) { + // console.log('fetch', url); + const promise = new XMLHttpRequestPromise(url, options); + promise.send(); return promise; } +export {fetch}; -export {XMLHttpRequestPromise, makeRequest as prepareXMLHttpRequestPromise}; -\ No newline at end of file