@dc65k

Как ограничить количество одновременных запросов на сервер?

Есть задача со следующими условиями:

1. Написать функцию, получающую на вход массив ссылок и число, указывающее максимальное количество одновременных запросов.
2. Одновременно должно выполняться не более указанного числа запросов.
3. Должен возвращаться массив результатов в той же последовательности, что и адреса запросов.
4. Нельзя делать повторные запросы на дублирующиеся адреса (при этом результат всё равно должен присутствовать в результирующем массиве).

makeRequest([
    'url1',
    'url2',
    'url1',
    'url3'
], 2).then(([
	    result1,
	    result2,
	    result1,
	    result3
	]) => {
});

Итак, моё решение:

const removingDuplicates = (arr) => {
    return arr.filter((item, i, arr) => arr.indexOf(item) === i)
}

function makeRequests (urls, maxNumberRequests) {
    const uniqueUrls = removingDuplicates(urls);
    // console.log(uniqueUrls);

    let response = null;

    uniqueUrls.forEach(async (url, idx) => {
        // console.log('url', url, idx);

        if (idx === maxNumberRequests) {
            return false;
        }

        response = await fetch(url, {
            method: 'POST',
            body: "statistics",
            keepalive: true
        });

        return response;
    });
}

makeRequests([
    'url1',
    'url2',
    'url1',
    'url3'
], 2);

Собственно, подскажите, что мне нужно подправить, полагаю, что я не совсем правильно понял условие задачи.
  • Вопрос задан
  • 679 просмотров
Решения вопроса 1
0xD34F
@0xD34F Куратор тега JavaScript
подскажите, что мне нужно подправить

Всё нужно подправить. А ещё больше - сделать.

Ваша функция никак не извещает вызывающий код о том, что запросы выполнены и с какими результатами; результаты запросов - вместо того, чтобы сохранять все, сохраняете только последний полученный; вместо того, чтобы ограничить количество одновременно выполняемых запросов вы пропускаете один (кажется, вы просто не понимаете, как работает forEach).

Пусть функция возвращает промис, который будет резолвиться после получения результатов всех запросов.

Под хранение результатов запросов создадим массив.

Так как url'ы могут повторяться, а запросы не должны, выполним группировку массива url'ов в объект: ключи - url'ы, значения - массивы индексов, под которыми данный url встречается в исходном массиве. Выдернем из этого объекта ключи - это будет массив уникальных url'ов.

Также создадим два счётчика - количество отправленных запросов и количество полученных ответов.

Собственно запрос. Первым делом проверяем, сколько ответов получено - если все, резолвим промис массивом результатов. В противном случае проверяем, сколько запросов отправлено - если не все, увеличиваем соответствующий счётчик, отправляем запрос; по получении ответа достаём массив индексов url'а, сохраняем результат, увеличиваем счётчик полученных ответов и пытаемся отправить следующий запрос.

Ну и запускаем всё это дело в работу - обычный цикл for, в котором на каждой итерации выполняется запрос. Минимум одна итерация (это на тот случай, если массив url'ов пустой), максимум - количество уникальных url'ов или ограничение на количество одновременных запросов, меньшее из этих значений.

Всё:

function makeRequests(urls, max) {
  return new Promise(resolve => {
    const results = Array(urls.length).fill(null);
    const groupedUrls = urls.reduce((acc, n, i) => ((acc[n] ??= []).push(i), acc), {});
    const uniqueUrls = Object.keys(groupedUrls);
    let countRequests = 0;
    let countResponses = 0;

    for (let i = 0; i < Math.max(1, Math.min(max, uniqueUrls.length)); i++) {
      request();
    }

    function request() {
      if (countResponses === uniqueUrls.length) {
        resolve(results);
      } else if (countRequests < uniqueUrls.length) {
        const url = uniqueUrls[countRequests++];
        fetch(url)
          .then(result => result.json())
          .catch(error => error)
          .then(result => {
            groupedUrls[url].forEach(n => results[n] = result);
            countResponses++;
            request();
          });
      }
    }
  });
}

Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
firedragon
@firedragon
Не джун-мидл-сеньор, а трус-балбес-бывалый.
Вас не смущает что яваскрипт однопоточный? Так что одновременно никак, но ! Он и событийный а события в нашем мире имеют разную длительность. Поэтому по факту вы должны обернуть каждый запрос в promise и ждать выполнения всех, позже отсортировать результат или отобразить ошибку или отобразить только валидные данные
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы