Cancel previous requests and only fire the latest request with redux observable

孤街浪徒 提交于 2019-12-18 12:38:42

问题


So I have use case where I update the api request when the map is moved - but it could generate several rapid fire requests with small map movements - and I want to cancel all the inflight requests except for the last one. I can use debounce to only send requests after a delay. However I still want to cancel any old requests if they happen to still be in process.

const fetchNearbyStoresEpic = action$ =>
  action$.ofType(FETCH_NEARBY_STORES)
    .debounceTime(500)
    .switchMap(action =>
      db.collection('stores')
        .where('location', '<=', action.payload.max).
        .where('location', '>=', action.payload.min)
        .map(response => fetchNearbyStoresFulfilled(response))
        .takeUntil(action$.ofType(FETCH_STORES_CANCELLED))
    );

I see that you can use takeUntil but you need to explicitly fire a cancel action. I see in the docs that switchMap will take the latest and cancel all the others - do I have to implement a cancel interface in my api call? In this case it would be a firebase query to firestore.


回答1:


From a comment I made in a GitHub issue:

Because they have a time dimension, there are multiple flattening strategies for observables:

  • With mergeMap (which has flatMap as an alias), received observables are subscribed to concurrently and their emitted values are flattened into the output stream.
  • With concatMap, received observables are queued and are subscribed to one after the other, as each completes. (concatMap is mergeMap with a concurrency of one.)
  • With switchMap, when an observable is received it's subscribed to and any subscription to a previously received observable is unsubscribed.
  • With exhaustMap, when an observable is received it's subscribed to unless there is a subscription to a previously received observable and that observable has not yet completed - in which case the received observable is ignored.

So, like Mark said in his answer, when switchMap receives a subsequent action, it will unsubscribe from any incomplete request.

However, the request won't be cancelled until the debounced action makes it to the switchMap. If you want to cancel any pending requests immediately upon another move - rather than wait for the debounce duration - you can use takeUntil with the FETCH_NEARBY_STORES action:

const fetchNearbyStoresEpic = action$ =>
  action$.ofType(FETCH_NEARBY_STORES)
    .debounceTime(500)
    .switchMap(action =>
      db.collection('stores')
        .where('location', '<=', action.payload.max).
        .where('location', '>=', action.payload.min)
        .map(response => fetchNearbyStoresFulfilled(response))
        .takeUntil(action$.ofType(FETCH_NEARBY_STORES))
    );

That should effect the immediate unsubscription from a request upon another move. (Off the top of my head, I cannot recall the behaviour of action$ in redux-observable. It's possible that you might need to append a skip(1) to the observable passed to takeUntil. Try it and see.)

And, as Mark mentioned, this is predicated on the underlying implementation cancelling the request upon unsubscription.




回答2:


switchMap will abandon its previous observable when a new emission is send through it. Depending on your underlying HTTP library and if it supports cancellation (Observable aware) this should suffice.

Because no implementation details have been provided in your question you will have to look into fetchNearbyStoresFulfilled to see if it uses an Observable aware http client. If it internally is using promises then no cancellation support is provided.



来源:https://stackoverflow.com/questions/50160430/cancel-previous-requests-and-only-fire-the-latest-request-with-redux-observable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!