Home > Software design >  How to make sure an HTTP request within an effect is finished before proceeding
How to make sure an HTTP request within an effect is finished before proceeding

Time:01-08

I have a question regarding making sure that an HTTP request call completes before proceeding any further to future lines of code. When I dispatch the action:

this._appStore.dispatch(SomeActionHere({status: contactStatus, contactId: this.contactId}));

I want to make sure that the HTTP Post request from the someOtherService.update function in the effects file is finished before I call the shouldBeCalledAfter function from someService. I would ideally want to do this without some type of BehaviorSubject. To clarify, the SomeActionHere effect is triggered after dispatching the action: SomeActionHere.

onClick(selectedOption?: {displayName: string, value: LogCallSubmitOptionsEnum}) {
    const contactStatus$ = this.callLogStore.select(getContactStatus).pipe(take(1));
    ... 
    forkJoin([contactStatus$, ...])
      .subscribe(([contactStatus]) => {
        this._appStore.dispatch(SomeActionHere({status: contactStatus, contactId: this.contactId}));
        ... 

        this.someService.shouldBeCalledAfter(callLogInfo, authToken.userId, logCallAndComplete, undefined, this.contactId, this.isManualLog)
          .subscribe((res) => {
            ...
          }, err => {
            ...
          });
      });
  }

Effects file:

someActionHereEffect$ = createEffect(() =>
    this.action$.pipe(
      ofType(SomeActionHere),
      mergeMap(({ id }) =>
        this.someOtherService.update(id).pipe(
          map((res) => {
            ...
          catchError((error: any) => {
           ...
          }
          )
        )
      )
    )
  );

Is there any way to make sure that an HTTP request call from the effects file is finished before proceeding to the someService.shouldBeCalledAfter function?

CodePudding user response:

Yes, setup another effect for this.

// your first effect
someActionHereEffect$ = createEffect(() =>
  this.action$.pipe(
    ofType(SomeActionHere),
    switchMap(({ id }) =>
      this.someOtherService.update(id).pipe(
        map((res) => {
          ...
          // fire your new effect here
          return new AnotherActionSayingThisIsDone(payload)
        },
        catchError((error: any) => {...})
      )
    )
  )
);

// Your new effect
anotherEffect = createEffect(() => {
  this.actions$.pipe(
    ofType(AnotherActionSayingThisIsDone)
    ...
})

CodePudding user response:

As mentioned in my previous comment, the right way to go is to create a new effect and dispatch an action from first, capture from the new one and do the logic there. That way you keep consistent and follow the conventions of the pattern and tool you are using.

If you want to do it with BehaviorSubject, which I don't recommend you, you can do it easily as well, just create a BS (perhaps in a service?):

private notifySource = new BehaviorSubject<boolean>(false);
public notify$ = this.notifySource.asObservable();

next() {
  this.notifySource.next(true);
}

Then from the effect, you just emit a new value to that BS:

this.someOtherService.update(id).pipe(
      map((res) => {...}),
      tap(() => notifyService.next()), // This will trigger new stream

And being subscribed to the BS you will get notified, then just trigger the new call or logic you want to do after the first effect/api call:

notifyService.notify$.pipe(
   takeWhile(v => !!v), // to skip the first case where stream is false (initialization of BS)
   // Use switchMap to avoid anti-pattern subscribe inside
   switchMap(() => this.someService.shouldBeCalledAfter(callLogInfo, authToken.userId, logCallAndComplete, undefined, this.contactId, this.isManualLog))  subscribe
).subscribe((res) => console.log(res)) // response from shouldBeCalledAfter method
  •  Tags:  
  • Related