{"action":"created","issue":{"url":"https://api.github.com/repos/yelouafi/redux-saga/issues/644","repository_url":"https://api.github.com/repos/yelouafi/redux-saga","labels_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/644/labels{/name}","comments_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/644/comments","events_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/644/events","html_url":"https://github.com/yelouafi/redux-saga/pull/644","id":190538139,"number":644,"title":"Saga Error Handling Example","user":{"login":"granmoe","id":5620706,"avatar_url":"https://avatars.githubusercontent.com/u/5620706?v=3","gravatar_id":"","url":"https://api.github.com/users/granmoe","html_url":"https://github.com/granmoe","followers_url":"https://api.github.com/users/granmoe/followers","following_url":"https://api.github.com/users/granmoe/following{/other_user}","gists_url":"https://api.github.com/users/granmoe/gists{/gist_id}","starred_url":"https://api.github.com/users/granmoe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/granmoe/subscriptions","organizations_url":"https://api.github.com/users/granmoe/orgs","repos_url":"https://api.github.com/users/granmoe/repos","events_url":"https://api.github.com/users/granmoe/events{/privacy}","received_events_url":"https://api.github.com/users/granmoe/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":5,"created_at":"2016-11-20T04:05:58Z","updated_at":"2016-12-12T15:45:31Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/yelouafi/redux-saga/pulls/644","html_url":"https://github.com/yelouafi/redux-saga/pull/644","diff_url":"https://github.com/yelouafi/redux-saga/pull/644.diff","patch_url":"https://github.com/yelouafi/redux-saga/pull/644.patch"},"body":"Use spawn to start sagas so that an uncaught exception doesn't terminate all of them. Restart a saga on an asynchronous exception. Terminate it on a sync exception.\r\n\r\n```javascript\r\nexport default function* root() {\r\n  yield fork(startup)\r\n  const sagas = [nextRedditChange, invalidateReddit]\r\n\r\n  yield sagas.map(saga =>\r\n    spawn(function* () {\r\n      let isSyncError = false\r\n      while (!isSyncError) {\r\n        isSyncError = true\r\n        try {\r\n          setTimeout(() => isSyncError = false)\r\n          yield call(saga)\r\n        } catch (e) {\r\n          if (isSyncError) {\r\n            throw new Error(saga.name + ' was terminated because it threw an exception on startup.')\r\n          }\r\n          yield put(actions.setError(e.message))\r\n        }\r\n      }\r\n    })\r\n  )\r\n}\r\n```\r\n\r\nIf you simply `yield call` your sagas within your root saga, then any uncaught exception in any of them will kill all of them, making your app unresponsive. To isolate each saga from the others, I use `spawn`. This way, an error in one doesn't affect any of the others, and they remain responsive.\r\n\r\nHowever, we still don't want the saga that had an uncaught exception to die because maybe some crazy async error was thrown that we somehow didn't add specific handling for. To deal with this, we can add a `while (true) { try { yield call(saga) } catch (e) { } }` that will restart the saga after handling its error. But if the error is simply something that is thrown when the saga starts (we make a typo during development, for example, and try to access a property from `undefined`), we don't want to start this saga, causing it to immediately throw the error, catch the error, and repeat forever. To check if the error is sync, I added the `isSyncError` flag that only gets set to `false` in a deferred manner, after the current stack is cleared, via `setTimeout`.\r\n\r\nI've wrestled with this problem quite a bit, and I can't come up with any better solution. I would be happy to see some alternatives, but this is a significant pain point that just isn't addressed in any of the documentation or examples. Therefore, I think we need to offer an idiomatic way of solving it in this example.\r\n\r\nThis is a resurrection of #610, which I had closed in order to get a different pull request in in the meantime (can't have two PRs at once)."},"comment":{"url":"https://api.github.com/repos/yelouafi/redux-saga/issues/comments/266464791","html_url":"https://github.com/yelouafi/redux-saga/pull/644#issuecomment-266464791","issue_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/644","id":266464791,"user":{"login":"Andarist","id":9800850,"avatar_url":"https://avatars.githubusercontent.com/u/9800850?v=3","gravatar_id":"","url":"https://api.github.com/users/Andarist","html_url":"https://github.com/Andarist","followers_url":"https://api.github.com/users/Andarist/followers","following_url":"https://api.github.com/users/Andarist/following{/other_user}","gists_url":"https://api.github.com/users/Andarist/gists{/gist_id}","starred_url":"https://api.github.com/users/Andarist/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Andarist/subscriptions","organizations_url":"https://api.github.com/users/Andarist/orgs","repos_url":"https://api.github.com/users/Andarist/repos","events_url":"https://api.github.com/users/Andarist/events{/privacy}","received_events_url":"https://api.github.com/users/Andarist/received_events","type":"User","site_admin":false},"created_at":"2016-12-12T15:45:31Z","updated_at":"2016-12-12T15:45:31Z","body":"I really like @aikoven approach. It makes sense to have steer things like this.\r\n\r\nUnhandled exceptions is not a problem of saga itself, but more of a general coding problem.\r\n\r\n@granmoe If your solution works for you, it should be fine for you to use it I think. or do you have some issues with it?\r\n\r\nAlso see a commentary here - https://github.com/yelouafi/redux-saga/issues/632#issuecomment-262352406 about `onError`. Maybe that feature could be utilised for more general use cases as some kind of global-saga try/catch? WDYT? How would you see it working in various scenarious? Differences between fork/spawn?\r\n"}}
{"actor":{"display_login":"Andarist"}}