-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add possibility to truly cancel/dispose Fututre #42855
Comments
There is no The code above is not cancelling any futures. It's cancelling a cancelable operation, but you forgot to link the for (int j = 0; j < 10; j++) {
bool isCancelled = false;
Future<void> longOne = () async {
await Future.delayed(Duration(seconds: 1));
if (isCancelled) return;
int i = 0;
int b = 0;
Random c = Random(j);
while (++i < 42949672) {
b = c.nextInt(i);
}
print("Debug: index: $j");
return;
}().timeout(Duration(milliseconds: 100)).catchError((e) {
print(e);
});
CancelableOperation cancelableOperation = CancelableOperation.fromFuture(longOne, onCancel: () {
print("cancel called");
isCancelled = true;
});
cancelableOperation.cancel();
} That's how a To change futures so that they can be cancelled directly, and make the future represent the entire computation would be a significant change. There would need to be some kind of All in all, definitely more complicated, and not something that an be compiled directly to JavaScript promises. |
This was intented as a showcase :) So in that moment the only way to somehow stop "Future operations" is move computation into new isolate, am I right? |
Yes, an isolate is the only kind of computational unit that you can actually cancel from the outside in Dart. |
You cannot use CancelableOperation everywhere. This removes any benefits of async/await syntax (not even taking to account nested awaits). It also very hard to wrap everything when multiple await calls are happening as it will lead to spaghetti callbacks code. Frankly, I'm a little surprised that if was not foreseen when designing async/await. This cancellation really limits where await can be used. It would be really nice to implement something like cancelable coroutines in Kotlin (or Tasks in C#). They just throw CancellationException on suspension points (aka await calls). This greatly helps with resource cleanup when no-one needs it anymore. |
That's basically what The future |
I'm talking about complex logic with multiple nested awaits that can't be moved to a
How is coroutines cancellation in Kotlin or tasks in C# fragile? |
When we designed Future resourceUser() async* {
var resource = await allocateResource();
try {
...do something with resource ...
... yield something ...
} finally {
resource.release();
}
} This code is currently safe. If the allocation fails, the code throws. If the allocation succeeds, the code enters the Even if cancelling at the The current design for futures is not intended to solve cancelling. It never was, even before adding Could we have done differently from the start? Probably. We could have added That's just not what the goal of |
The problem is not propagating cancellation upwards ( Future<void> foo() async {
await bar();
}
Future<void> bar() async {
await baz();
}
Future<void> baz() async {
// ...
}
void main() {
foo().cancel();
} Here cancellation should propagate from downwards all the way to It is not clear to me if there is anything prevents us from defining the following semantics:
I would assume this is what Kotlin does. UPDATE: an example with |
@mraleph, at very least minimal api to check whether current Future async chain is cancelled would be really nice. That way developers can design "interruption" points themself. For ex.
isAsyncCancelled is a really lightweight alternative to the coroutineContext.isActive from Kotlin that can be accessed only from async functions. All old code will still work, nothing will break. But it will be possible to manually check for future cancellation at "safe" points. |
We could add magic and let The idea of introducing |
Cancelling a chain is fine - try recursing down and if the final future returns That's a problem we have already had with |
Here's a quick first-draft of a possible proposal for cancelable futures: It will contain typos. (And isn't something I expect us to be able to pull off any time soon). Dart Cancelable FuturesThere have been many requests for cancelable futures in Dart, where you can directly write This is a proposal for a change to Dart which implements this and integrates it into the ProposalFuture APIWe add a /// Attempts to cancel the operation behind this future.
///
/// Returns `true` if the operation is cancelled and `false`
/// if cancelling the operation is not possible.
/// If the cancel is successful, further calls to this method
/// will return `true` again and do nothing further
///
/// Some asynchronous code may not be in a state where it is possible
/// to abort its operation.
/// Perhaps it has already started an unstoppable external operation,
/// or it has allocated a resource which the caller is intended to release,
/// or maybe the computation has already completed the future,
///
/// If cancellation is successful, the underlying operation is interrupted
/// in some way, and will *likely* complete this future with a [CancelError]
/// error. It may choose to do any number of other things, including
/// not completing the future at all, but the operation creating such
/// a future should document its behavior.
bool cancel(); To receive the cancellation, we extend /// Sets the cancel callback.
///
/// The cancel callback is called if the [future] of this completer
/// is cancelled using [Future.cancel].
///
/// This callback is not called again after it has returned `true` once.
/// At this point, the future is considered cancelled, and nothing can
/// change that again. Further calls to [Future.cancel] on [future]
/// will then return `true` and do nothing further.
///
/// If no cancel callback is set, the default is a function returning `false`.
set onCancel(bool cancel());
/// Whether the future of this completer has been successfully cancelled.
///
/// Whether the [future] has been *successfully* cancelled using [Future.cancel],
/// meaning that the [onCancel] callback was called and returned `true`.
bool get isCancelled; It's still the responsibility of the creator of the Even if We also add: abstract class CancelError implements Error {} to Propagating cancelsA future doesn't need to be created using a Each such operation is updated to propagate the cancel operation as well as possible. Example: Example: Example: Async/await behaviorAn This call will (most likely) happen while the function body is blocked on an If blocked on an If blocked on an If the Inside an async function, like If someone calls
We should probably choose the latter, and only refuse to cancel if another awaited future prevents it. CostAll this obviously adds overhead. Every The completer needs to retain more state (at least one bit to remember whether it has successfully been cancelled, and one word for the The state machines used by our There will be a number of extra |
Good proposal but I didn't get one thing about
|
This is API design, it has to cover all possible invocation sequences. We can't assume that no-on will try to cancel a future multiple times. The If you are only using The issue that I'm trying to handle with returning If you do (I didn't actually add a way to check whether a |
Main concern here is that all nested Maybe it is even better for users to throw
|
Almost 2 month have passed. Is there any updates on this issue? As an additional example for this feature you can look no further that into 'http' package published by 'dart.dev'. You can send requests there only via futures: final response = await http.read('http://some_url') The only way to cancel request is to call Current |
A feature like this is not currently on our short-list of priorities. Also, as I stated above, it likely needs the interface default methods language feature (or something similar) for it to be viable for us to add members to |
Any update on this issue? |
No updates, still not something we are working actively towards. I'm not expecting that to change in the near future. Allowing arbitrary futures to be cancelled is a deep change, with not easily-predicted consequences. |
Based on our discussions with @lrhn and consistent interest in this feature I have written in a proposal for how we could add some support for cancellation into the language: https://gist.github.com/mraleph/6daf658c95be249c2f3cbf186a4205b9 @lrhn could you take a look and let me know what you think? |
Hi,
In current version of dart there is possibility to cancel Future, using
CancelableOperation
. But this solutions do only one thing, cancels listening for result.There is missing possibility to truly cancel working Future.
Eg. This small piece of code will run, even all futures are canceled:
The text was updated successfully, but these errors were encountered: