getify / fasy

FP iterators that are both eager and asynchronous
MIT License
545 stars 18 forks source link

Is there a way to make operations cancelable? #6

Open getify opened 6 years ago

getify commented 6 years ago

Explore some way to combine/bring in CAF (cancelable async functions) capabilities so that fasy's async iterations are optionally cancelable.

This might need to be a separate namespace where the API methods assume the cancelable token being passed in, or it may just be that existing methods could be polymorphic in some way.

Also to be explored: should fasy re-implement the cancelation, or should there be an adapter that can be optionally applied which delegates to CAF (which would need to be present separately) when needed.

TheSench commented 6 years ago

What is the expectation when an operation is cancelled?

  1. Would it return what it has retrieved so far?
  2. Would it return an empty result?
  3. Would it throw some kind of "Operation Aborted" exception?
  4. "And now for something completely different?"

Another consideration: what does cancelling a request mean to fasy? I assume if it were an asynchronous request to a server, you'd want to make sure that not only do you stop waiting for its promise to resolve, but that you actually abort the underlying request.

I'm guessing you'd want to ability to cancel a chain of requests, regardless of where in the flow you are (e.g. if you chained a concurrent map on getOrders for a list of users into another concurrent map on getOrderDetails on those orders, you'd probably want to use the same code to just "cancel it", regardless of which map it's on). If that's the case, perhaps add a method onto FA that returns a wrapped FA that has a cancel method and that adds its cancellation token to any requests made using its concurrent, serial, or transducers methods.

At my company, we were using some AngularJS services sitting atop $ajax to make batched OData calls and had to figure out how to cancel the operation if the user decided to change routes before the load finished. I ended up creating a function on our service that would wrap it up with a context (basically $scope and a pending request collection). Any $ajax requests made using that wrapper would be created with a cancellation promise. They would add that promise to the list of pending requests and remove it when they themselves resolved. If $scope got destroyed (or if the code using the wrapper explicitly asked to cancel requests), the cancellation promises would be resolved (and the pending $ajax calls would be aborted).

getify commented 5 years ago

Sorry for taking so long to respond. I think your observations make sense.

Since all fasy iterators return promises for their results, I think the most sensible thing is that canceled operations result in rejecting that promise.

Still trying to consider the ergonomics of specifying the CAF-style cancelation tokens.