Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Async / await also need a idiom to accompany on how to support cancellation. In practice, cancellation happens a lot because the unbounded latency for async operations. Without a throughout support, async / await syntax is probably usable on server side somewhat but still hardly applicable on client side (as the latency is unbounded). On the other hand, C# does go through the pain and added cancellation support to all its standard libraries async API.


I can't upvote this enough. In practice, supporting cancellation is one of the most important and tedious parts of asynchronous programming.

It's easy to start a some task with a completion handler. Even error handling isn't that hard, since communication of a failure goes in the same direction as communicating success.

But cancellation goes into the other direction! So whenever you start an async operation, you need to somehow store a handle or something, so you can cancel it later on.

The actor model makes this even worse -- how do you cancel a command that you previously sent? How do you tell an actor that they don't need to perform an operation, if the operation is still on the queue, or that they should abort the operation if they are already working on it?

If the actor model doesn't have an answer to this problem, developers won't be able to use it as is, and they will have to build additional abstractions on top of it before they can use it.


You create an actor that performs an operation and simply kill it if you want to cancel that operation. Promises/futures with cancellable contexts are equivalent to actors.


Yes and no. You can for sure implement actors which support cancellation or killing them, the only question is how. In lots of actor frameworks you send the actor a close message that triggers it to shut down - and you can also send it a cancel message after some request. However if the server side actor is not implemented fully asynchronously and doesn't read it's messages, it might take a long time for the cancellation to happen -> exactly the same time which it takes for the operation to finish. E.g. if the remote actor is implemented in some sequence like: receiveRequest(); doSomethingWithRequestWhichIsProbablyBlockingRequestsToOtherActors(); sendResponse(); then there is no cancellation possibility. If the other actor is implemented fully asynchronously then it could pick up the cancellation and do something with it. But it's harder to implement, becaue then you have again state machines everywhere. In erlang killing actors without messages might work, because it's supported deeply within the runtime.


I'm not sure I understand. How would that make sense? An actor would typically be a shared resource (eg. the main thread, a database connection, etc). You can't just kill the main thread because you want to cancel a command that you sent earlier.


To implement killing is not easy.

Edit: to implement it in a well-performing way.


This is not true: Erlang is a counter-example.


Is the implementation in Erlang remarkably simple? How does it work?


I don't think we should get too focused on cancellation.

It's inherently a half-measure: it's used to avoid wasting resources, but it only comes into play after you've already wasted resources. If you want to minimize waste, you're going to do better if you can minimize initiating operations that end up needing to be canceled.

Not that it isn't a useful refinement. It should be planned for. But I don't think it can be considered a critical feature out of the gate.


This is incorrect. Cancelling is not just about preventing wasted resources. It's about reacting to a change in circumstances. You can't predict ahead of time that the user will change their mind. You can't know ahead of time how long a call will block.

If you want to write an app that feels responsive, you must be able to cancel operations.


To be mediately responsive you simply abandon the operation -- that is, stop waiting and ignore any result. No need to cancel anything.

In case you're talking about rolling back an operation (you probably weren't, but just in case), canceling doesn't help there either -- not generally -- since you don't know how far the operation got before canceling, how long the cancel will take to propagate (or if it can at all), etc.


"stop waiting and ignore the result" only works if the operation is something short lived that doesn't consume many resources, eg. sending a REST request.

If the operation is expensive, that doesn't work. Say, if the user clicks a link to view a 2GB file, then changes their mind to view a different file instead, you really don't want the browser to continue downloading that 2GB file only to discard the result afterwards.

But the problem is that pretty much any operation is potentially expensive. Sending a REST request might become expensive when your phone has poor signal. All of the sudden all those "inexpensive" REST calls you just ignored are queuing up only to have their responses discarded when they arrive several seconds later, wasting bandwidth when you need it most.

If the language doesn't make cancelling easy, developers won't support cancelling -- and then you end up with unresponsive apps.


That assumes distributed environment (for example, server or microservices). CPU / memory / bandwidth is finite on client-side applications (maybe it is more Swift heavy actually) and you cannot simply stop waiting and not waste resources.

Yeah, cancellation in most systems are a "patch" (as you said, you don't know how long it will take to propagate (again, assuming distributed system, but this is less relevant when we talk about multicore system, with multicore system, that really is just about when a boolean propagated to all cores from false to true) but a necessary one and need to be integrated deep in the runtime. C# as an example, passed cancellation context (I believe in C# they called cancellation token) through all their async API as optional parameter. Maybe this is something Swift should take some inspirations from and will be beneficial for client-side developers.


Maybe... but not everything can be cancelled.

What if you are too late and, say, asynchronous write to a disk was already commited and the data is out of your program and on the drive?

And how do you cancel a sent email?

At some point there is no sense in sending a "cancel" message, or there is no way of cancelling an action. How would you proceed in such a situation?


You are misunderstanding why cancelling is necessary. Cancelling is not for undoing stuff. Cancelling is necessary to avoid waiting for asynchronous operations that have become unnecessary.

Eg. imagine that a document is auto-saved in the background, but the network is busy. Then the user changes the document. Now the app wants to auto-save the new version, but the previous version hasn't been saved yet. Saving the previous version no longer makes any sense and would just waste time, so you want to cancel the previous save operation, and save the new version instead. (it doesn't matter if cancelling the previous save is successful or not -- all that matters is that you don't want to wait for the previous action)

Or imagine that an app opens and tries to show the last document that the user displayed. The document is big, so the app shows a loading indicator. But the user doesn't want to actually view the document, he wants to view a different document, so he closes the document and opens a different one. Now the app needs to cancel loading the first document, because otherwise it would take much longer to load the second document that the user actually wants to see.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: