1

I need to perform an action, after retrieving 2 objects from my store (ngrx), so, I need both calls to have responded before performing my action, like this :

const item1$: Observable<Item> = this._store$.select(
  ItemStoreSelectors.selectItemById(this.id1)
);

const item2$: Observable<Item> = this._store$.select(
  ItemStoreSelectors.selectItemById(this.id2)
);

let item1: Item;
item1$.pipe(take(1)).subscribe((item: Item) => {
  item1 = item;
});

let item2: Item;
item2$.pipe(take(1)).subscribe((item: Item) => {
  item2 = item;
});

// here, items might not have been initialized
doSomething(item1, item2);

I tried to look out for a solution in rxjs with switchMap, mergeMap, etc, but couldn't apply it to my needs. I think I found a solution doing async/await, but I'm not sure this is a good practice.

Thanks for your help.

3
  • 2
    use combineLatest learnrxjs.io/operators/combination/combinelatest.html Commented Jul 26, 2019 at 11:58
  • 1
    or depending on the case... forkJoin. Commented Jul 26, 2019 at 11:58
  • @MoxxiManagarm gonna look into that, thank you, I've already tried I think but couldn't get it to work with my case Commented Jul 26, 2019 at 12:00

3 Answers 3

3

Refactor into the following code :

import { forkJoin } from 'rxjs';

const item1$: Observable<Item> = this._store$.select(
  ItemStoreSelectors.selectItemById(this.id1)
);

const item2$: Observable<Item> = this._store$.select(
  ItemStoreSelectors.selectItemById(this.id2)
);

forkJoin([item1$.pipe(take(1)), item2$.pipe(take(1))])
   .subscribe(([item1,item2])=>doSomething(item1,item2));
  
Sign up to request clarification or add additional context in comments.

2 Comments

thank you it looks awesome, so if I understand well, subscribe will only fire when both items have been fired ?
Correct, forkJoin creates an observable that emits the last value of its sources after all of them completed.
1

combineLatest will be triggered every time one of the observable fire, if this is what you want, try this out:

item1$: Observable<Item> = this._store$.select(
  ItemStoreSelectors.selectItemById(this.id1)
);

item2$: Observable<Item> = this._store$.select(
  ItemStoreSelectors.selectItemById(this.id2)
);

result$: Observable<any> = combineLatest(this.item1$, this.item2$, this.doSomething());

1 Comment

thank you as well, I'm gonna look into the difference between combineLatest and forkJoin as @Jota. Toledo answered but I think it doesn't answer completely to my needs because I need a pipe(take(1))
0

Alternatively you could use withLatestFrom in an effect. Something like this

@Effect()
  myAction$ = this.actions$.pipe(
    ofType<MyAction>(MyActionTypes.MyAction),
    withLatestFrom(this.store.select(mySelector)),
    withLatestFrom(this.store.select(myOtherSelector)),
    exhaustMap(([[action, item1], item2]) =>
      this.myService.myRequest(/*...*/)
      .pipe(
        map(myResult => new GetResultSuccess(myResult)),
        catchError(error => of(new GetResultFailure(error))),
      ),
    ),
  );

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.