redux-saga: uncaught error from Promise.reject
A demo here.
Code related below, the race
code mostly comes from https://github.com/yelouafi/redux-saga/issues/183:
import { fork, call, put, race, take } from 'redux-saga/effects'
function * api () {
try {
let result = yield * callWithLoadingEffect(fetchApi)
console.log(result)
} catch (error) {
console.log('error', error)
}
}
// fetch data from reddit
function fetchApi () {
return fetch('https://www.reddit.com/new.json')
.then(
(response) => {
return Promise.reject('haha')
// return response.json()
}
)
.catch(
(error) => Promise.reject(error)
)
}
const delay = (ms) => new Promise((resolve) => setTimeout(() => resolve(true), ms))
export function * callWithLoadingEffect (fn) {
try {
const task = yield fork(fn)
const taskPromise = new Promise((resolve, reject) => {
task.done.then(resolve, reject)
})
let {timeout, result} = yield race({
timeout: call(delay, 100),
result: taskPromise
})
if (timeout) {
yield put({
type: 'SHOW_LOADING'
})
result = yield taskPromise
}
yield put({
type: 'HIDE_LOADING'
})
return result
} catch (err) {
yield put({
type: 'HIDE_LOADING'
})
throw err
}
}
export function * helloSaga () {
console.log('Hello Sagas!')
while (true) {
yield take('FETCH')
yield fork(api)
}
}
If I returned Promise.reject('haha')
when response comes back, proc.js
would print errors:

I thought the try {} catch () {}
in callWithLoadingEffect
would catch the error as doc said, and from the console, I can see api
generator did catch the error, but that uncaught haha
seems like that it isn’t true, any suggest?
Thanks.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 1
- Comments: 16 (16 by maintainers)
I think you can get the same effect and a simpler code with try/finally
IMO, you shouldn’t use any try/catch with a join effect. The main purpose of a join is to wait for a task to terminate. The task should handle all its errors and let only bubble unexpected ones (see below)
I’d rather separate errors in 2 classes
At least the best way to work with the redux-saga fork model is to handle business errors from inside the forked tasks themselves an let bubble only bugs which can be caught at some higher level.
In fact I can even go farther and recommend to not use try/catch for business error handling because javascript IMO lacks the necessary constructs for typed catch blocks. consider this code
because the catch block catches all errors: both business Errors (server error, timeout …) and bugs (Api.gett is not function). The code reacts the same on both types; I don’t think this the desired behavior. Typically we want to put an Error action only on server errors and let bubble any bug. In typed languages like Java you can achieve this with typed catch blocks like
catch (ServerError err)
But JS lacks this feature.The best way to think of the fork model is as dynamic parallel effect. A saga has a main flow (its own body => the main task) and can fork parallel flows dynamically. Like in parallel effects, cancelling the saga will cancel all the parallel tasks (main task + forked tasks). The saga will terminate after all parallel tasks terminate, and an error from one of the tasks will abort the whole parallel effects.
It may sound restrictive but this has the advantage of having a much simpler model to reason about. You know precisely how do Return values, Errors and Cancellations propagate, … The other option is the very flexible Promise model but also its well known issues (esp. Error propagation => unhandled rejections, error swallowed, not to mention the difficulty to define a simple and consistent model for Cancellations)