Как сгладить вложенные вызовы в RXJS и, наконец, вызвать один последний вызов?

#javascript #asynchronous #callback #rxjs #nested

#javascript #асинхронный #обратный вызов #rxjs #вложенный

Вопрос:

Как лучше всего мы можем сгладить приведенные ниже вызовы. Новичок в RxJS, пытаюсь понять, как его следует упростить. Прочитайте flatMap, forkJoin, switchMap и mergeMap, не получив правильного пути для его интеграции ниже и не уверен, что лучше в приведенном ниже сценарии.

 const useful = [];

a.get('abc').
subscribe((abcdatas) => {

   abcdatas.forEach(abcdata => {
     if(abcdata.exist) {
        b.get('def').
        subscribe((defdatas) => {
           useful.push(defdatas.someval);
        });
      } 
   });

 })

if(useful.length) {
 c.get('ghi').
 subscribe((ghidata) => {
   completed...
 });
}
  

Обновить

Обновляю свой вопрос здесь и благодарю за все ответы. Полезным является глобальный массив результатов, который в моем случае должен быть заполнен из вложенного вызова. И, наконец, он должен быть передан последнему вызову.

Шаги, которые я пытаюсь:

  1. a.get() => возвращает adata
  2. b.get(adataset) => должен выполнить запрос для каждого adataset, если adataset имеет атрибут exist, а также заполнить полезный массив, который будет использоваться позже
  3. c.get(полезный) => должен сработать и завершиться.

Ответ №1:

Используйте функцию сопоставления, например switchMap or mergeMap , для сопоставления результата от одного запроса к следующему запросу. Используется forkJoin для одновременного выполнения нескольких запросов.

Итак, для сценария «один ко многим» общая идея такова:

 firstRequest().pipe(
  switchMap(results => forkJoin(results.map(r => nextRequest(r))))
)
  

Для вашего случая это было бы что-то вроде:

 useful = [];

a.get('abc').pipe(
  switchMap(abcdatas => forkJoin(getUseFulRequests(abcdatas))),
  tap(useful => useful.forEach(u => this.useful.push(u))),
  switchMap(useful => useful.length ? c.get('ghi') : EMPTY)
).subscribe((ghidata) => {
  completed...
});


function getUseFulRequests(abcdatas: AbcData[]): Observable<SomeVal>[] {
  return abcdatas.reduce((acc, abcdata) => {
    if (abcdata.exist) {
      const request = b.get('def').pipe(
        map(defdatas => defdatas.someval)
      )
      acc.push(request);
    }
    return acc;
  }, []);
}
  

Это ничего не выдаст, если getUseFulRequests(abcdatas) возвращает пустой массив или useful.length == 0 .

Комментарии:

1. Ценю ваш ответ

2. полезно не только для полезных запросов, это массив, сформированный вторым вызовом, т.е. get()

3. @MithunShreevatsa вы можете использовать tap для заполнения вашего глобального массива из промежуточного ответа.

4. Пожалуйста, пример, как я уже сказал, я новичок, и мне требуется время, чтобы выйти из axios и async, await и promises

5. спасибо, я узнал кое-что очень полезное из вашего ответа и сделал для меня чистый вызов 🙂

Ответ №2:

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

Рассмотрим приведенный ниже код

 useful$ = a.get('abc').pipe(
  mergeMap(abcdatas => 
    abcdata.exist ? forkJoin(abcdatas.map(abcdata => b.get('def'))) : of(undefined)
  ),
  map(defdatas => defdatas.flat()),
  mergeMap(({ length }) => length ? c.get('ghi') : of(undefined))
);

useful$.subscribe({
  next: () => { 
    // Completed...
  }
})
  

Сначала мы передаем результат a.get('abc') и используем mergeMap для проверки, если abcdata.exist . Если он завершается, мы возвращаем forkJoin(abcdatas.map(abcdata => b.get('def'))) просто, это объединит массив наблюдаемых, сгенерированных из функции map в abcdatas

map(defdatas => defdatas.flat()), преобразует массив в один массив ПРИМЕЧАНИЕ: flat() был представлен в ES2019

Затем мы разрушаем length свойство и, если оно существует, возвращаем наш окончательный наблюдаемый

Комментарии:

1. Ценю ваш ответ

Ответ №3:

Я думаю, что то, что вы пытаетесь сделать, это:

 a.get("abc").pipe(
  mergeMap((abcdatas) => abcdatas.filter((abcdata) => abcdata.exist)), // let's create a stream with all those useful abcdata
  mergeMap(abcdata => b.get('def')), // and for each one of those we perform a b.get request
  toArray(), // once all the b.get requests have completed, emit a one value stream with an Array of those values values
  concatMap(useful => useful.length ? c.get('ghi') : EMPTY) // let's concat that result with the final request
)
  

Комментарии:

1. Ценю ваш ответ