angularangular-redux

Delay epic action depending on payload


According to the documentation it is quite simple to delay an action within an observable:

const pingEpic = action$ =>
  action$.ofType('PING')
    .delay(1000) // Asynchronously wait 1000ms then continue
    .mapTo({ type: 'PONG' });

But I'd like to delay the action depending on some value within the action payload. for example something like this (what doesn't work):

const pingEpic = action$ =>
  action$.ofType('PING')
    .delay(action => action.payload.delayTime) // Asynchronously wait the time defined within the action then continue
    .mapTo({ type: 'PONG' });

Unfortunately .delay() only takes a simple static value and I don't know how to set the value from the action. Someone any idea?

Why do I need it?

The reason behind this behaviour is, that I'm calling a REST API within my epic, which creates a new item in the backend. When this call returns I'm going to trigger two action (through flatMap). One succeeded action to let the reducer update the corresponding state and a second action to get a list of all available items within the backend. Unfortunately is the backend a little bit slower (and I have no control over it), so that an immediate get call for all items returns a list without the freshly created item. Doing the same call 500ms later returns anything as expected. Cause this "get the whole list request" is exactly the same as it is when the page is shown the first time, I don't like to have this delay by all requests, but only when it is triggered after a create call. So my idea was to add a delay parameter to the action (which default value is zero) which will then be taken into account from the epic.

If nobody can tell me how to get the delay from the action, the second possibility would be to use a different action where another epic simply listens and use some kind of hard-coded delay, but it would be cool if I could parametrize this through the same action.


Solution

  • You can do this using delayWhen, but it's a bit clunky (it works similar to takeUntil):

    const pingEpic = action$ => action$.ofType('PING')
      .delayWhen(({ payload: { delayTime }}) => Observable.timer(delayTime)) 
      .mapTo({ type: 'PONG' });
    

    So delayWhen delays your original Observable until the returned Observable by the callback function emits something. Then it resumes the ORIGINAL Observable, so if you had a map instead of a mapTo after, you could map on the original action/payload/whatever.