Ограничить максимальное число параллельных запросов
Имеется следующая функция:
export function getImage(requestParameters: MapLibreRequestParameters): MapLibreRequest<MapLibreResponse<ImageBitmap | HTMLImageElement>> {
const request = helper.getArrayBuffer(requestParameters);
return {
response: (async () => {
const response = await request.response;
const image = await arrayBufferToCanvasImageSource(response.data);
return {
data: image,
cacheControl: response.cacheControl,
expires: response.expires
};
})(),
cancel: request.cancel
};
}
Она синхронная, но возвращает объект, состоящий из двух полей: response
- Promise
, который резолвится объектом (3 поля: data, cacheControl, expires
, но это для нас неважно) и cancel
- метод, который отменяет запрос.
Эта функция работает как нужно и все с ней хорошо. Однако, мне нужно реализовать дополнительное ограничение. Нужно сделать так, чтобы количество параллельных (одновременных) запросов к сети в любой момент времени не превышало n
.
Таким образом, если n === 0
, не должно быть сделано ни одного запроса. Если n === 1
, то одновременно может загружаться только одно изображение (то есть все изображения загружаются последовательно). При n > 1 < m
одновременно загружаться могут не более m
изображений.
Мое решение
Исходя из того что функция getImage
синхронная, то строка
const request = helper.getArrayBuffer(requestParameters);
выполняется сразу же при вызове getImage
. Такое нам не нужно, нам нужно выполнение самого запроса отложить. Поэтому переменную request
заменим на функцию requestMaker
, которую будем вызывать только тогда, когда нам это нужно:
export function getImage(requestParameters: MapLibreRequestParameters): MapLibreRequest<MapLibreResponse<ImageBitmap | HTMLImageElement>> {
if (webpSupported.supported) {
if (!requestParameters.headers) requestParameters.headers = {};
requestParameters.headers['Accept'] = 'image/webp,*/*';
}
function requestMaker() {
const request = helper.getArrayBuffer(requestParameters);
return request;
}
return {
response: (async () => {
const response = await requestMaker().response;
const image = await arrayBufferToCanvasImageSource(response.data);
return {
data: image,
cacheControl: response.cacheControl,
expires: response.expires
};
})(),
cancel() {
//
}
};
}
(cancel
пока опустим).
Теперь выполнение этой функции requestMaker
, делающей сам запрос, нужно отложить до какого-то момента.
Предположим, что сейчас мы пытаемся решить задачу только для n === 1
.
Создадим массив, в котором будем хранить все запросы, выполняющиеся в данных момент:
const ongoingImageRequests = [];
Теперь внутри requestMaker
будем сохранять в эту переменную запросы как только они происходят, и удалять их как только мы получаем ответ:
const ongoingImageRequests = [];
export function getImage(requestParameters: MapLibreRequestParameters): MapLibreRequest<MapLibreResponse<ImageBitmap | HTMLImageElement>> {
if (webpSupported.supported) {
if (!requestParameters.headers) requestParameters.headers = {};
requestParameters.headers['Accept'] = 'image/webp,*/*';
}
function requestMaker() {
const request = helper.getArrayBuffer(requestParameters);
ongoingImageRequests.push(request);
request.response.finally(() => ongoingImageRequests.splice(ongoingImageRequests.indexOf(request), 1));
return request;
}
return {
response: (async () => {
const response = await requestMaker().response;
const image = await arrayBufferToCanvasImageSource(response.data);
return {
data: image,
cacheControl: response.cacheControl,
expires: response.expires
};
})(),
cancel() {
//
}
};
}
Осталось лишь добавить ограничение касательно запуска requestMaker
: перед тем как запустить ее, нужно дождаться await
выполнения всех запросов из массива:
const ongoingImageRequests = [];
export function getImage(requestParameters: MapLibreRequestParameters): MapLibreRequest<MapLibreResponse<ImageBitmap | HTMLImageElement>> {
if (webpSupported.supported) {
if (!requestParameters.headers) requestParameters.headers = {};
requestParameters.headers['Accept'] = 'image/webp,*/*';
}
function requestMaker() {
const request = helper.getArrayBuffer(requestParameters);
ongoingImageRequests.push(request);
request.response.finally(() => ongoingImageRequests.splice(ongoingImageRequests.indexOf(request), 1));
return request;
}
return {
response: (async () => {
await Promise.allSettled(ongoingImageRequests.map(ongoingImageRequest => ongoingImageRequest.response));
const response = await requestMaker().response;
const image = await arrayBufferToCanvasImageSource(response.data);
return {
data: image,
cacheControl: response.cacheControl,
expires: response.expires
};
})(),
cancel() {
//
}
};
}
Я понимаю это так: при начале выполнения getImage
(она вызвана откуда-то извне), она сразу же возвращает объект, в котором response
- это Promise
, который разрешится как минимум не раньше того момента, когда будут выполнены все остальные запросы из очереди.
Но, как выходит, такое решение почему-то не работает. Вопрос - почему? И как сделать так, чтобы оно работало? Хотя бы для n === 1
.