Как предотвратить одновременный запуск нескольких http-запросов - PullRequest
2 голосов
/ 18 мая 2019

У меня есть массив объектов.Для каждого объекта мне нужно вызвать асинхронный запрос (HTTP-вызов).Но я хочу, чтобы одновременно выполнялся только определенный максимум запросов.Кроме того, было бы хорошо (но не обязательно), если бы я мог иметь одну единственную точку синхронизации после того, как все запросы закончили выполнять некоторый код.

Я попробовал предложения от:

Ограничить количество запросов одновременно с помощью RxJS

Как ограничить параллелизм flatMap?

Запустить асинхронный запрос параллельно, но получить результат взаказ с использованием rxjs

и многих других ... Я даже пытался создавать свои собственные операторы.

Либо ответы на этих страницах слишком стары, чтобы работать с моим кодом, либо я могу 'Я не могу понять, как собрать все воедино, чтобы все типы прекрасно подходили друг другу.

Это то, что у меня есть:

for (const obj of objects) {
  this.myService.updateObject(obj).subscribe(value => {
    this.anotherService.set(obj);
  });
}

РЕДАКТИРОВАТЬ 1: Хорошо, я думаюмы добираемся туда!С ответами Юлиус и pschild (оба, кажется, работают одинаково) мне удалось ограничить количество запросов.Но теперь он будет запускать только первую партию из 4, а остальные - никогда.Итак, теперь у меня есть:

const concurrentRequests = 4;
from(objects)
  .pipe(
    mergeMap(obj => this.myService.updateObject(obj), concurrentRequests),
    tap(result => this.anotherService.set(result))
  ).subscribe();

Я что-то не так делаю с subscribe()?

Кстати: параметр mergeMap с resultSelector устарел, поэтому я использовал mergeMap без этого.Кроме того, obj из mergeMap не отображается в tap, поэтому мне пришлось использовать параметр tap

EDIT 2:

Убедитесь, что ваши наблюдатели завершили!(Это стоило мне целый день)

Ответы [ 3 ]

3 голосов
/ 18 мая 2019

Вы можете использовать третий параметр mergeMap, чтобы ограничить количество одновременных внутренних подписок. Используйте finalize для выполнения чего-либо после завершения всех запросов:

const concurrentRequests = 5;
from(objects)
    .pipe(
        mergeMap(obj => this.myService.updateObject(obj), concurrentRequests),
        tap(res => this.anotherService.set(res))),
        finalize(() => console.log('Sequence complete'))
    );

См. Пример на Stackblitz .

2 голосов
/ 18 мая 2019
from(objects).pipe(
  bufferCount(10),
  concatMap(objs => forkJoin(objs.map(obj => 
    this.myService.updateObject(obj).pipe(
      tap(value => this.anotherService.set(obj))
  )))),
  finalize(() => console.log('all requests are done'))
)

Код не тестировался, но вы поняли идею.Дайте мне знать, если потребуется какая-либо ошибка или объяснение

0 голосов
/ 18 мая 2019

Однажды у меня была такая же проблема.Когда я пытался загрузить несколько изображений с сервера.Мне приходилось отправлять http запросы один за другим.Я достиг желаемого результата, используя ожидаемое обещание.Вот пример кода:

async ngOnInit() {
    for (const number of this.numbers) {
      await new Promise(resolve => {
        this.http.get(`https://jsonplaceholder.typicode.com/todos/${number}`).subscribe(
          data => {
            this.responses.push(data);
            console.log(data);
            resolve();
          }
        );
      });
    }
  }

Основная идея здесь, чтобы разрешить обещание, как только вы получите ответ.С помощью этой техники вы можете создать собственную логику для выполнения одного метода после завершения всех запросов.

Вот stackblitz .Откройте консоль, чтобы увидеть ее в действии.:)

...