Is there any rethrow (not throw) equivalent for futures in dart?
myFunc()
.then(processValue)
.catchError(handleError);
Yes.
If the handleError of a catchError throws the same error object again (because it acts the same way as then's onError), it is considered a "rethrow" and it will also retain the original stack trace.
If you just want to catch some types of errors, and rethrow the rest, you can use the test parameter of catchError:
myFunc()
.then(processValue)
.catchError(handleError, test: (e) => e is MyException);
This only catches MyException exceptions and lets any other error pass through, with no need for you to rethrow them. The test can do any testing, not just type checks.
Also, if you use async functions, you can just use rethrow:
...) async {
try {
var processValue = await myFunc();
....
} catch (e) {
if (something(e)) {
whatnot();
} else {
rethrow;
}
}
Related
My understanding of catching errors was that if a certain error is 'caught', the code in a method following the catch block would not execute unless you have a finally statement afterwards.
However, if I catch a certain error type, but don't have a return or a throw/rethrow statement at the end, it does seem to keep going with the further code in the method.
I am catching a certain exception type, with the statement on CustomException catch (error) but I'm not planning to rethrow the error, and I don't need to do anything with the actual error. I don't have a return or a rethrow statement, as I assumed the catch block. I have two questions to make sure I'm understanding this behavior properly:
First question - if I don't plan to use the specific error inside my catch block, does adding catch (error) after on CustomException actually make a difference? It seems like it's ok to do it with or without it.
Second question - does my catch block need to have a return/throw/rethrow statement at the end in all cases to prevent the method from performing any further code?
Code example 1: With the catch (error) statement:
try {
...
} on CustomException catch (error) {
...
} catch (error) {
rethrow;
}
print('Is this method still running?');
Code example 2: without the catch (error) statement:
try {
...
} on CustomException {
...
} catch (error) {
rethrow;
}
print('Is this method still running?');
Your two examples aren't the same; one catches CustomException and the other catches PlatformException. Presuming that you meant them to be the same, then the difference between on CustomException and on CustomException catch (error) is that the second way allows you to do something with the thrown object (or if you use on CustomException catch (error, stacktrace), to do something with the stacktrace too).
The Dart Language Tour covers this:
Use on when you need to specify the exception type. Use catch when your exception handler needs the exception object.
Additionally, doing:
catch (error) {
rethrow;
}
is unnecessary noise. Just omit it and let the thrown exception go uncaught.
Problem:
I have the following function which shows a warning No calls to throwing functions occur within 'try' expression
Questions:
Why is this warning shown? (The code inside the task throws an error)
What should I do to propagate the error to the caller of f1?
Code:
func f1() async throws {
try await withThrowingTaskGroup(of: Int.self) { group in //No calls to throwing functions occur within 'try' expression
group.addTask(priority: .high) {
throw NSError()
}
}
}
You indeed have not called any throwing functions, or thrown anything, in the withThrowingTaskGroup closure. You've just added a task for the group to run. This by itself won't throw anything. addTask is not marked throws, though its closure parameter is. It will only throw when you wait for the task's completion in one way or another.
For example:
try await group.waitForAll()
Or if you want to loop through each task of the group:
for try await someInt in group {
// ...
}
If you return something in the withThrowingTaskGroup closure, that's what withThrowingTaskGroup will return too. If you throw an error in the closure, withThrowingTaskGroup will throw that error (since it is rethrows), and so will f1.
I am trying to create a Dart function that essentially wraps other functions with some boilerplate error handling code, and otherwise returns the value returned by the original function. A key requirement is that it should accept functions with multiple different return types, while avoiding duplicating the common error handling logic across multiple different functions. I found one approach that seems to work by using the dynamic type, except that the compiler is not able to detect type mismatches, so they are only caught at runtime.
Is there a better way to accomplish what I'm aiming for here, and particularly in a way that catches type mismatches at compile time?
Below is a simplified example of my code, where the functions compile fine, but at runtime getAString will raise an error Dart Error: Unhandled exception: type 'List<String>' is not a subtype of type 'String'
/// Signature of API function calls
typedef APIFunctionCall = dynamic Function();
dynamic doWithErrorHandling(APIFunctionCall fn, {retries: 2}) async {
for (int attempts = 0; attempts < retries + 1; attempts++) {
try {
return await fn();
}
on Exception catch (e) {
print(
"This is just an example; actual function does a bunch of more specific error handling.");
}
}
}
Future<String> getAString() async {
// Want a function that can support multiple return types but detect type errors
String doesReturnAString = await doWithErrorHandling(() async => 'hello world'); // This runs fine
String doesntReturnAString = await doWithErrorHandling(() async => <String>['hello', 'world']); // This throws an Error
return doesntReturnAString;
}
You can abstract over the return type using a type parameter:
Future<T> doWithErrorHandling<T>(Future<T> fn(), {int retries = 2}) async {
do {
try {
return await fn();
} catch (e) {
// record error.
}
retries--;
} while (retries >= 0);
return null; // or whatever.
}
With that, you can call with any function. In most cases, the type argument can be inferred from the static type of the argument function, or from the type expected by the surrounding context, but if not, you can write it yourself.
Future<String> getAString() async {
String doesReturnAString = await doWithErrorHandling(() async => 'hello world');
// The next line has a compile-time type error!
String doesntReturnAString = await doWithErrorHandling(() async => <String>['hello', 'world']);
return doesntReturnAString;
}
(As an unrelated hint, you should never catch Exception. Dart errors do not implement Exception, they implement Error. Exception is a meaningless marker interface used by some thrown objects that the user is intended to catch and handle, but in that case, you should be catching the particular exception, like on FormatException, not the plain Exception. So, general rule: Never write on Exception).
In my Production code, I am getting errors in my logs when a Mono times out.
I have managed to recreate these errors with the following code:
#Test
public void testScheduler() {
Mono<String> callableMethod1 = callableMethod();
callableMethod1.block();
Mono<String> callableMethod2 = callableMethod();
callableMethod2.block();
}
private Mono<String> callableMethod() {
return Mono.fromCallable(() -> {
Thread.sleep(60);
return "Success";
})
.subscribeOn(Schedulers.elastic())
.timeout(Duration.ofMillis(50))
.onErrorResume(throwable -> Mono.just("Timeout"));
}
In the Mono.fromCallable I am making a blocking call using a third-party library. When this call times out, I get errors similar to
reactor.core.publisher.Operators - Operator called default onErrorDropped
reactor.core.publisher.Operators - Scheduler worker in group main failed with an uncaught exception
These errors also seem to be intermittent, sometimes when I run the code provided I get no errors at all. However when I repeat the call in a loop of say 10, I consistently get them.
Question: Why does this error happen?
Answer:
When the duration given to the timeout() operator has passed, it throws a TimeoutException. That results in the following outcomes:
An onError signal is sent to the main reactive chain. As a result, the main execution is resumed and the process moves on (i.e., onErrorResume() is executed).
Shortly after outcome #1, the async task defined within fromCallable() is interrupted, which triggers a 2nd exception (InterruptedException). The main reactive chain can no longer handle this InterruptedException because the TimeoutException happened first and already caused the main reactive chain to resume (Note: this behavior of not generating a 2nd onError signal conforms with the Reactive Stream Specification -> Publisher #7).
Since the 2nd exception (InterruptedException) can't be handled gracefully by the main chain, Reactor logs it at error level to let us know an unexpected exception occurred.
Question: How do I get rid of them?
Short Answer: Use Hooks.onErrorDropped() to change the log level:
Logger logger = Logger.getLogger(this.getClass().getName());
#Test
public void test() {
Hooks.onErrorDropped(error -> {
logger.log(Level.WARNING, "Exception happened:", error);
});
Mono.fromCallable(() -> {
Thread.sleep(60);
return "Success";
})
.subscribeOn(Schedulers.elastic())
.timeout(Duration.ofMillis(50))
.onErrorResume(throwable -> Mono.just("Timeout"))
.doOnSuccess(result -> logger.info("Result: " + result))
.block();
}
Long Answer: If your use-case allows, you could handle the exception happening within fromCallable() so that the only exception affecting the main chain is the TimeoutException. In that case, the onErrorDropped() wouldn't happen in the first place.
#Test
public void test() {
Mono.fromCallable(() -> {
try {
Thread.sleep(60);
} catch (InterruptedException ex) {
//release resources, rollback actions, etc
logger.log(Level.WARNING, "Something went wrong...", ex);
}
return "Success";
})
.subscribeOn(Schedulers.elastic())
.timeout(Duration.ofMillis(50))
.onErrorResume(throwable -> Mono.just("Timeout"))
.doOnSuccess(result -> logger.info("Result: " + result))
.block();
}
Extra References:
https://tacogrammer.com/onerrordropped-explained/
https://medium.com/#kalpads/configuring-timeouts-in-spring-reactive-webclient-4bc5faf56411
I'm trying to really get Futures in Dart and I've noticed that just about every example I come across uses handleException to deal with exceptions that complete the Future. Yet the API documentation states "In most cases it should not be necessary to call handleException, because the exception associated with this Future will propagate naturally if the future's value is being consumed. Only call handleException if you need to do some special local exception handling related to this particular Future's value."
So when would I need "special local exception handling"? Could someone explain that in a bit more detail? Is there some code that I honestly can't run easily by letting the exception propagate?
Mads Ager gave me this answer:
Basically, this is the equivalent of having a try-catch in straight-line code:
int doSomethingElse() {
try {
return thisMightFail();
} catch(e) {
return -1;
}
}
void doSomething() {
int value = doSomethingElse();
// operate on value
}
With Futures it is something like this (not tested):
Future<int> doSomethingElse() {
return thisMightFail().transformException((e) => -1);
}
void doSomething() {
doSomethingElse().then((value) {
// operate on value
});
}
So this is for local exception handling instead of global exception handling. If you never use handleException or transformException that would correspond to always dealing with exceptions at the top level in non-async code.