William Perron

William Perron

Head in the clouds.

16 Feb 2024

It's All About Cancellation

Asynchronous vs synchronous programming is a hot topic these days, and arguably always was. We talk about things like function coloring and argue over async implementations left and right. We fight over whether the best way to deal with asynchronous code is to make await a keyword to slap in front of a function or a sort of method attached to a Future or Promise or to simply not have any distinction and let the caller decide (think Goroutines).

What’s almost always left out of those conversations is the fact that asynchronous code is just plain different than synchronous code. It behaves differently. As soon as you make a function asynchronous, you now have to contend with the everything that comes with that choice. What happens if the program needs to exit while the asynchronous function runs? What if we want to make sure that it won’t run forever without ever stopping?

Take Go for example, a language where functions may be asynchronous under the hood without the caller having to even know about it. If, like me, you’ve spent any amount of time working with Go, you’ve likely noticed that code that depends on asynchronous behavior is littered with context.Context, and for good reason: We want to stop ongoing tasks when the program receives the SIGTERM signal, or we want to stop waiting for a response after some timeout.

That’s cancellation for you. Cancellation is table stake when it comes to asynchronous programming. And at the end of the day, no matter what language you use, or the features that it has, or however much it “colors” its concurrent functions, you will have to deal with this gnarly, complicated, tedious stuff.

Asynchronous programming is inherently different, let’s stop pretending it isn’t. Futures (or Promises) are hard, because concurrency is hard.