sync_throw_in_async.
Description
A non-async function which returns a Future should not also throw synchronously.
Details
Users expect functions to be either synchronous or asynchronous, the latter being recognized by returning a Future.
If a function returns a Future, users may choose to call catchError on the future, and assume that to catch all errors.
However, a non-async function which returns a Future can also choose to throw synchronously instead of
returning future containing an error.
Users will likely not expect that, and won't catch the error.
The lint will trigger on a throw statement in the body of a non-async function which (statically) returns a Future type.
(Does not apply to methods which may return a future only if a type variable ends up bound to a future type. That has its own potential problems, but this lint is not trying to fix those. Such a function is not considered "asynchronous", it's just passing futures around as values.)
Kind
This lint guards against errors by providing consistent and predictable APIs to users, where an asynchronous function (returning a Future) always provides its entire result in that future, and doesn't throw independently of that.
It means callers don't need two layers of error handling, which they'll most likely forget to do.
Good Examples
Future<int> validateParse(String input) async { // Be async.
if (!isValid(input)) throw FormatException("Bad format"); // Error returned in future.
return await asyncParse(input);
}
Future<int> validateParse2(String input) {
// Return a future with the error.
if (!isValid(input)) return Future.error(FormatException("Bad format"));
return asyncParse(input);
}
Bad Examples
Future<int> validateParse(String input) { // Not `async`, returns `Future`.
if (!isValid(input)) throw FormatException("Bad format"); // Throws directly.
return asyncParse(input);
}
Discussion
This has come up multiple times, and it's a rule that the platform libraries try to enforce (haven't always, and that's also always caused problems).
See #50361 for one example of this behavior not being expected.
It's not a big issue if people use async for all functions which interact with futures, but that's not required. (The discarded_futures lint).
Discussion checklist
Should not directly affect other lints.
#50361 is one example. I'm sure there have been others.
There are other lints that restrict "something" around async in non-async functions (discarded_futures disallows creating a future in a non-async function). This lint would recognize non-async functions with a Future return type.
It's, slightly surprisingly, not something we have said explicitly,
It's a general recommendation that I've repeated many times, that a function returning a Future should return all its errors in that future, but it's not written on the Future class, and not easily found in our other async documentation.
(Should probably fix that.)
The issue above shows one person hitting this. I have seen others.
The problem is usually subtle, because it only shows if an error happens. Most of the time, no error happens, so you won't recognize the issue. The someone, somewhere, uses your library slightly wrong, and they can't catch the error using Future.catchError.
sync_throw_in_async.Description
A non-
asyncfunction which returns aFutureshould not also throw synchronously.Details
Users expect functions to be either synchronous or asynchronous, the latter being recognized by returning a
Future.If a function returns a
Future, users may choose to callcatchErroron the future, and assume that to catch all errors.However, a non-
asyncfunction which returns aFuturecan also choose to throw synchronously instead ofreturning future containing an error.
Users will likely not expect that, and won't catch the error.
The lint will trigger on a
throwstatement in the body of a non-asyncfunction which (statically) returns aFuturetype.(Does not apply to methods which may return a future only if a type variable ends up bound to a future type. That has its own potential problems, but this lint is not trying to fix those. Such a function is not considered "asynchronous", it's just passing futures around as values.)
Kind
This lint guards against errors by providing consistent and predictable APIs to users, where an asynchronous function (returning a
Future) always provides its entire result in that future, and doesn't throw independently of that.It means callers don't need two layers of error handling, which they'll most likely forget to do.
Good Examples
Bad Examples
Discussion
This has come up multiple times, and it's a rule that the platform libraries try to enforce (haven't always, and that's also always caused problems).
See #50361 for one example of this behavior not being expected.
It's not a big issue if people use
asyncfor all functions which interact with futures, but that's not required. (Thediscarded_futureslint).Discussion checklist
Should not directly affect other lints.
#50361 is one example. I'm sure there have been others.
There are other lints that restrict "something" around async in non-
asyncfunctions (discarded_futuresdisallows creating a future in a non-asyncfunction). This lint would recognize non-asyncfunctions with aFuturereturn type.It's, slightly surprisingly, not something we have said explicitly,
It's a general recommendation that I've repeated many times, that a function returning a
Futureshould return all its errors in that future, but it's not written on theFutureclass, and not easily found in our other async documentation.(Should probably fix that.)
The issue above shows one person hitting this. I have seen others.
The problem is usually subtle, because it only shows if an error happens. Most of the time, no error happens, so you won't recognize the issue. The someone, somewhere, uses your library slightly wrong, and they can't catch the error using
Future.catchError.