Skip to content

Commit

Permalink
advance image request queue when request aborted (#7641)
Browse files Browse the repository at this point in the history
and silence AbortError when using fetch api
  • Loading branch information
ansis authored Nov 30, 2018
1 parent 16a0c1d commit 90577a9
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 7 deletions.
36 changes: 30 additions & 6 deletions src/util/ajax.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import window from './window';
import { extend } from './util';
import { isMapboxHTTPURL } from './mapbox';
import config from './config';
import assert from 'assert';

import type { Callback } from '../types/callback';
import type { Cancelable } from '../types/cancelable';
Expand Down Expand Up @@ -110,6 +111,10 @@ function makeFetchRequest(requestParameters: RequestParameters, callback: Respon
callback(new AJAXError(response.statusText, response.status, requestParameters.url));
}
}).catch((error) => {
if (error.code === 20) {
// silence expected AbortError
return;
}
callback(new Error(error.message));
});

Expand Down Expand Up @@ -175,8 +180,12 @@ function sameOrigin(url) {

const transparentPngUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';

const imageQueue = [];
let numImageRequests = 0;
let imageQueue, numImageRequests;
export const resetImageRequestQueue = () => {
imageQueue = [];
numImageRequests = 0;
};
resetImageRequestQueue();

export const getImage = function(requestParameters: RequestParameters, callback: Callback<HTMLImageElement>): Cancelable {
// limit concurrent image loads to help with raster sources performance on big screens
Expand All @@ -187,17 +196,25 @@ export const getImage = function(requestParameters: RequestParameters, callback:
}
numImageRequests++;

// request the image with XHR to work around caching issues
// see https://github.com/mapbox/mapbox-gl-js/issues/1470
return getArrayBuffer(requestParameters, (err: ?Error, data: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => {

let advanced = false;
const advanceImageRequestQueue = () => {
if (advanced) return;
advanced = true;
numImageRequests--;
assert(numImageRequests >= 0);
while (imageQueue.length && numImageRequests < config.MAX_PARALLEL_IMAGE_REQUESTS) { // eslint-disable-line
const {requestParameters, callback, cancelled} = imageQueue.shift();
if (!cancelled) {
getImage(requestParameters, callback);
}
}
};

// request the image with XHR to work around caching issues
// see https://github.com/mapbox/mapbox-gl-js/issues/1470
const request = getArrayBuffer(requestParameters, (err: ?Error, data: ?ArrayBuffer, cacheControl: ?string, expires: ?string) => {

advanceImageRequestQueue();

if (err) {
callback(err);
Expand All @@ -215,6 +232,13 @@ export const getImage = function(requestParameters: RequestParameters, callback:
img.src = data.byteLength ? URL.createObjectURL(blob) : transparentPngUrl;
}
});

return {
cancel: () => {
request.cancel();
advanceImageRequestQueue();
}
};
};

export const getVideo = function(urls: Array<string>, callback: Callback<HTMLVideoElement>): Cancelable {
Expand Down
26 changes: 25 additions & 1 deletion test/unit/util/ajax.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
getArrayBuffer,
getJSON,
postData,
getImage
getImage,
resetImageRequestQueue
} from '../../../src/util/ajax';
import window from '../../../src/util/window';
import config from '../../../src/util/config';
Expand Down Expand Up @@ -128,5 +129,28 @@ test('ajax', (t) => {
window.server.requests[0].respond();
});

t.test('getImage cancelling frees up request for maxParallelImageRequests', (t) => {
resetImageRequestQueue();

window.server.respondWith(request => request.respond(200, {'Content-Type': 'image/png'}, ''));

const maxRequests = config.MAX_PARALLEL_IMAGE_REQUESTS;

// jsdom doesn't call image onload; fake it https://github.com/jsdom/jsdom/issues/1816
const jsdomImage = window.Image;
window.Image = class {
set src(src) {
setTimeout(() => this.onload());
}
};

for (let i = 0; i < maxRequests + 1; i++) {
getImage({url: ''}, () => t.fail).cancel();
}
t.equals(window.server.requests.length, maxRequests + 1);
window.Image = jsdomImage;
t.end();
});

t.end();
});

0 comments on commit 90577a9

Please sign in to comment.