ayanami icon indicating copy to clipboard operation
ayanami copied to clipboard

How to avoid infinit self calls

Open stkevintan opened this issue 6 years ago • 3 comments

if we invoke else service method suck as this.getActions() inside the effectActions[actionName] function in sync way, it will cause an error:

InternalError: "too much recursion"

the effect can be:

  @Effect()
  fetchInfo(url$: Observable<string>):Observable<EffectAction> {
    return url$.pipe(
      switchMap(url => from(client.get(url))),
      mergeMap(data => of(this.getActions().setData(data), this.getActions().setLoading(false))),
      startWith(this.getActions().setLoading(true)),
    )
  }

the effect init function is :

https://github.com/LeetCode-OpenSource/ayanami/blob/b7b644bf960f5d0b9451045ce3a65631ace9fc24/src/core/ikari.ts#L196

because the startWith is sync, so this.getActions() will request the ikari instance, but the ikari instance is not fully constructed at that time. so it will invoke the create function again and agian...

stkevintan avatar Jun 17 '19 04:06 stkevintan

I try to fix it by rewrite some souce code as follow, but I dont known if it has some bad side effects or not

from: https://github.com/LeetCode-OpenSource/ayanami/blob/b7b644bf960f5d0b9451045ce3a65631ace9fc24/src/core/ikari.ts#L194

to:

   const payload$ = new BehaviorSubject<any>(undefined)
    actions[actionName] = (payload: any) => {
      payload$.next(payload)
    }
    const effect$: Observable<EffectAction> = from(Promise.resolve()).pipe(
      switchMap(() => effectActions[actionName](payload$, state$))
    )

stkevintan avatar Jun 17 '19 05:06 stkevintan

@Effect()
  fetchInfo(url$: Observable<string>):Observable<EffectAction> {
    return url$.pipe(
      switchMap(url => from(client.get(url))),
      mergeMap(data => of(this.getActions().setData(data), this.getActions().setLoading(false))),
-     startWith(this.getActions().setLoading(true)),
    )
  }

I think that the issue is caused by the startWith operator. How about:

@Effect()
  fetchInfo(url$: Observable<string>):Observable<EffectAction> {
    return url$.pipe(
      switchMap(url => from(client.get(url))),
      mergeMap(data => of(
        this.getActions().setLoading(true),
        this.getActions().setData(data),
        this.getActions().setLoading(false)
      )),
    )
  }

Maybe we should show some tips about misuse the startWith operator? 🤔️

runjuu avatar Jun 17 '19 07:06 runjuu

@Runjuu I think we should tell user not use this in sync context include startWith, such as :

@Effect()
fetchInfo(url$: Observable<string>):Observable<EffectAction> {
    this.getState()    //  Error 
    this.getState$()  //  Error
    this.getActions() //  Error
    return url$.pipe(
      switchMap(url => from(client.get(url))),
      mergeMap(data => of(
        this.getActions().setLoading(true),
        this.getActions().setData(data),
        this.getActions().setLoading(false)
      )),
    )
  }

or we can do something avoid this error ?

stkevintan avatar Jun 17 '19 09:06 stkevintan