Как избежать вложенной подписки rxJS - PullRequest
2 голосов
/ 16 мая 2019

Я запустил новый проект Angular 7, в котором мне нужно вызвать API, чтобы получить список результатов.Затем, если конкретный результат находится в списке, я выбираю его, если нет, создаю его, вызывая другую конечную точку API.Все это должно быть сделано в методе ngOnInit().

Чтобы упростить задачу, у меня есть следующий код:

const subject = new BehaviorSubject(null);
subject.next(6);

const res$ = subject.pipe(
  mergeMap(() => getValues()),
  tap((res) => {
    const exists = res.find(x => subject.value === x) || null;
    if (exists === null) {
      // TODO: call the getNew() function, but I don't want nested call
    } else {
      // Do nothing
    }
  })
);

// res$ should contains [1, 2, 3, 4, 5, 6]
res$.subscribe(x => console.log(x));

function getValues() {
  // Mocking the API call
  return of([1, 2, 3, 4, 5]);
}

function getNew() {
  // Mocking the API call
  return of(6);
}

В конце, наблюдаемое res$ должносодержит весь массив (с добавленным значением), и я хочу избежать вложенной подписки.

Большое спасибо!

Ответы [ 3 ]

2 голосов
/ 17 мая 2019

Две вещи:

  1. Не рассчитывайте на доступ к значению предмета. Вместо этого вы можете использовать альтернативную форму mergeMap, которая позволяет вам создать выбранное значение из источника и нового потока. В этом случае я использую его для возврата нового элемента вниз по течению с источником и значением из внутренней наблюдаемой.

  2. Вы можете использовать просто еще один mergeMap, чтобы вернуть либо исходный результат, либо результат нового вызова API, обернув результат orignal в of, чтобы создать новую внутреннюю наблюдаемую.

const res$ = subject.pipe(
  mergeMap(x => getValues(), (orig, res) => ({ orig, res })),
  mergeMap(({ orig, res }) => {
    const exists = res.find(x => x === orig) || null;
    return (exists) 
      ? of(res)
      : getNew().pipe(map(x => [... res, x]))
  })
);

Stackblitz

0 голосов
/ 16 мая 2019
const res$ = subject.pipe(
  mergeMap(x => getValues().pipe(
    mergeMap(values => values.find(v => v === x) 
      ? of(values)
      : getNew().pipe(map(n => [...values, n]))
    )
  ))
);

https://stackblitz.com/edit/rxjs-ch5iao

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

Один из способов решения этой проблемы может состоять в том, чтобы разделить это на две наблюдаемые, а затем объединить их в сочетании с CombatLatest.Мы будем использовать resFirst $ observable дважды и отфильтруем его, основываясь на том, равно ли оно нулю.

import { combineLatest } from 'rxjs';

---

const subject = new BehaviorSubject(null);
subject.next(6);

const resFirst$ = subject.pipe(
    mergeMap(() => getValues()),
    map((res) => res.find(x => subject.value === x) || null)
);

const resSecond$ = resFirst$.pipe(
    filter(exists => exists === null), <--- FILTERING FOR IS NULL
    mergeMap(() => getNew()) <--- SWITCHMAP COULD ALSO BE USED HERE DEPENDING ON WHAT YOU WANT
);

const final$ = combineLatest(resFirst$, resSecond$)
    .pipe(filter(exists => exists !== null)) <--- FILTERING FOR NOT NULL
    .subscribe(x => console.log(x));

function getValues() {
    // Mocking the API call
    return of([1, 2, 3, 4, 5]);
}

function getNew() {
    // Mocking the API call
    return of(6);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...