{"action":"created","issue":{"url":"https://api.github.com/repos/yelouafi/redux-saga/issues/266","repository_url":"https://api.github.com/repos/yelouafi/redux-saga","labels_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/266/labels{/name}","comments_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/266/comments","events_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/266/events","html_url":"https://github.com/yelouafi/redux-saga/issues/266","id":149838885,"number":266,"title":"Handle Cancellations in finally blocks?","user":{"login":"yelouafi","id":5453835,"avatar_url":"https://avatars.githubusercontent.com/u/5453835?v=3","gravatar_id":"","url":"https://api.github.com/users/yelouafi","html_url":"https://github.com/yelouafi","followers_url":"https://api.github.com/users/yelouafi/followers","following_url":"https://api.github.com/users/yelouafi/following{/other_user}","gists_url":"https://api.github.com/users/yelouafi/gists{/gist_id}","starred_url":"https://api.github.com/users/yelouafi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yelouafi/subscriptions","organizations_url":"https://api.github.com/users/yelouafi/orgs","repos_url":"https://api.github.com/users/yelouafi/repos","events_url":"https://api.github.com/users/yelouafi/events{/privacy}","received_events_url":"https://api.github.com/users/yelouafi/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/yelouafi/redux-saga/labels/discussion","name":"discussion","color":"207de5"},{"url":"https://api.github.com/repos/yelouafi/redux-saga/labels/enhancement","name":"enhancement","color":"84b6eb"},{"url":"https://api.github.com/repos/yelouafi/redux-saga/labels/feedback%20wanted","name":"feedback wanted","color":"fad8c7"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":3,"created_at":"2016-04-20T17:48:33Z","updated_at":"2016-04-21T15:13:27Z","closed_at":null,"body":"Actually, cancellations are thrown as exceptions inside cancelled tasks.\r\n\r\n```javascript\r\nfunction* saga() {\r\n  try {\r\n    const result = yield call(api)\r\n    yield put(successAction(result))\r\n  } catch(err) {\r\n    if(!isCancelError(err))\r\n      yield put(errorAction(err))\r\n  }\r\n}\r\n```\r\n\r\nThe check `isCancelError` is always necessary, even if we aren't interested in handling cancellations. Otherwise the saga will put an `errorAction` even is this is only a cancellation and not an api error. In practice I noticed that handling cancellations is pretty rare (most of the time we do nothing). While handling errors is more common. \r\n\r\nAlso cancellations do not have the same semantics as errors; Errors are values bubbling up from callees to callers, while cancellations are much like 'forced values' which propagate down from callers to callees. \r\n\r\nMy question is would it make more sense, and also would it be preferable to handle cancellations in finally blocs instead of catch blocks.\r\n\r\nSo for example we can write the previous example as\r\n\r\n```javascript\r\nfunction* saga() {\r\n  try {\r\n    const result = yield call(api)\r\n    yield put(successAction(result))\r\n  } catch(err) {\r\n    yield put(errorAction(err))\r\n  }\r\n}\r\n```\r\n\r\nThere is no need to `isCancelError` since the saga is not interested in handling the cancellation. Alternatively a saga that needs some special actions when it is cancelled could be written like this\r\n\r\n```javascript\r\nfunction* saga() {\r\n  try {\r\n    const result = yield call(api)\r\n    yield put(successAction(result))\r\n  } catch(err) {\r\n    yield put(errorAction(err))\r\n  } finally {\r\n    if( (yield status()) === CANCELLED)\r\n      // handle cancellations\r\n  }\r\n}\r\n```\r\n\r\nSince finally blocks do not accept arguments, we'll need to provide some way to check that w've been cancelled (finally will execute in 3 cases: normal return, unhandled error or 'forced return' i.e. cancellation). Above we use a `yield status()` effect.\r\n\r\nIt may look more awkward than the check inside catch, but I think the uses for this would be pretty rare (compared to catch usage)\r\n\r\nI've almost implemented all new changes in the project (channels, support SSR, attached forks ...); So far this is the only one concern left before making the next release and I'm not able to decide on it"},"comment":{"url":"https://api.github.com/repos/yelouafi/redux-saga/issues/comments/212965632","html_url":"https://github.com/yelouafi/redux-saga/issues/266#issuecomment-212965632","issue_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/266","id":212965632,"user":{"login":"yelouafi","id":5453835,"avatar_url":"https://avatars.githubusercontent.com/u/5453835?v=3","gravatar_id":"","url":"https://api.github.com/users/yelouafi","html_url":"https://github.com/yelouafi","followers_url":"https://api.github.com/users/yelouafi/followers","following_url":"https://api.github.com/users/yelouafi/following{/other_user}","gists_url":"https://api.github.com/users/yelouafi/gists{/gist_id}","starred_url":"https://api.github.com/users/yelouafi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yelouafi/subscriptions","organizations_url":"https://api.github.com/users/yelouafi/orgs","repos_url":"https://api.github.com/users/yelouafi/repos","events_url":"https://api.github.com/users/yelouafi/events{/privacy}","received_events_url":"https://api.github.com/users/yelouafi/received_events","type":"User","site_admin":false},"created_at":"2016-04-21T15:13:27Z","updated_at":"2016-04-21T15:13:27Z","body":"As a side note, using this approach I'm able to handle task Cancellations and Channel ending (see #255) using the same abstraction."}}