question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Handle Cancellations in finally blocks?

See original GitHub issue

Actually, cancellations are thrown as exceptions inside cancelled tasks.

function* saga() {
  try {
    const result = yield call(api)
    yield put(successAction(result))
  } catch(err) {
    if(!isCancelError(err))
      yield put(errorAction(err))
  }
}

The 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.

Also 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.

My question is would it make more sense, and also would it be preferable to handle cancellations in finally blocs instead of catch blocks.

So for example we can write the previous example as

function* saga() {
  try {
    const result = yield call(api)
    yield put(successAction(result))
  } catch(err) {
    yield put(errorAction(err))
  }
}

There 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

function* saga() {
  try {
    const result = yield call(api)
    yield put(successAction(result))
  } catch(err) {
    yield put(errorAction(err))
  } finally {
    if( (yield status()) === CANCELLED)
      // handle cancellations
  }
}

Since 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.

It may look more awkward than the check inside catch, but I think the uses for this would be pretty rare (compared to catch usage)

I’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

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:3
  • Comments:8 (5 by maintainers)

github_iconTop GitHub Comments

9reactions
yelouaficommented, May 2, 2016

@elliottsj To simulate cancellation call gen.return(). If the saga asks for the cancel status with a yield cancelled() resume it with true

//assuming this saga 
function* saga() {
  try {
    const resp = yield call(someApi)
    yield put(action(resp))
  } finally {
    if(yield cancelled()) {
       // handle logic specific to cancellation. For example
       yield <some effect>
    }
  }
}

// test
const gen = saga()
expect(gen.next().value).toEqual(call(someApi))
// simulates cancellation
// gen asking for cancel status
expect(gen.return().value).toEqual( cancelled() ) 
// answer yes, we've been cancelled
expect(gen.next(true).value).toEqual(<some effect>)
2reactions
martinkadlec0commented, May 1, 2017

@bradennapier I guess I am a bit late, but - just in case you haven’t bothered to find the answer later on - here is what happens: The javascript finally block is special because it gets executed not just every time when the code in try block succeeds or fails, but it is also called even if the code in try succeeds and there is a return statement there. However, any additional code after the try.catch.finally is not executed if the return is executed beforehand.

(function() {
  try {
    return 1;
  } finally {
    console.log('This gets executed.');
  }
  console.log('This does not get executed.');
})();

Now, the generators work pretty much the same, except that the return is called on the iterator.

genFn().return()
Read more comments on GitHub >

github_iconTop Results From Across the Web

Finally blocks run when the try block is cancelled
1. No, the finally block is designed specifically to be run either case. You can however catch specific exceptions types and handle differently ......
Read more >
Java Finally block - Exception handling - BeginnersBook
A finally block contains all the crucial statements that must be executed whether exception occurs or not. The statements present in this block...
Read more >
try-finally - C# Reference - Microsoft Learn
Within a handled exception, the associated finally block is guaranteed to be run. However, if the exception is unhandled, execution of the ...
Read more >
ERR05-J. Do not let checked exceptions escape from a finally ...
Methods invoked from within a finally block can throw an exception. Failure to catch and handle such exceptions results in the abrupt termination...
Read more >
The finally Block - Essential Java Classes
But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found