Redux-Saga race timeout leaves pending API request unresolved
0 reputation · 22 Mar 2023, 03:16 UTC
0 reputation · 22 Mar 2023, 03:16 UTC
When a Redux-Saga task races an asynchronous API call against a delay effect to implement a timeout, the timer should win cleanly and prevent any late response from updating state.
Cancelling a saga task interrupts the generator and runs finally blocks, but it does not automatically cancel the underlying Promise or network request passed to call. The request can continue after the saga treats the operation as timed out, creating a race between a delayed error action and a belated success response.
Does saga cancellation terminate the underlying fetch, or only the generator? Should the API call receive an AbortSignal from the timeout handler, and where should the controller be created relative to race? If the original promise resolves after the delay wins, what prevents its result from dispatching a state-changing action?
32775 reputation · 22 Mar 2023, 03:56 UTC
In a saga race the delay effect and the call(fetch…) effect run concurrently. If the delay wins, the saga is cancelled, but the underlying fetch promise keeps running unless you abort it yourself.
CancelledError into the generator; the promise is untouched.finally block runs.AbortController before the race and passed its signal to call(fetch, url, {signal}), calling abort() inside the finally will reject the promise with AbortError.SUCCESS action is dispatched.const controller = new AbortController() before the race.yield call(fetch, url, {signal: controller.signal}).const { timeout } = yield race({ timeout: delay(ms), fetch: call(...)});finally block, if timeout is truthy, call controller.abort() and yield cancel(fetchTask) (if you kept a reference).try/catch that ignores AbortError and does not dispatch a success action.Because the promise is rejected by AbortError, the catch block in the saga prevents the success branch from running. If you do not abort, the promise will resolve later; the saga’s generator has already finished, so the put that would update state is never executed.
Does your runtime’s fetch implementation honor AbortSignal (e.g., browsers, node-fetch, axios with fetch adapter)? If it does not, aborting will have no effect and the request will continue to run.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.