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.
Related
I have two service calls. The second one accepts a value that the first returns. I need to return the result of the first call only if the second succeeds. The following is my prototype implementation, however, the resulting mono is always empty. Please explain why it doesn't work and how to implement it the proper way.
#Test
public void testPublish() {
callToService1().publish(
mono -> mono.flatMap(resultOfCall1 -> callToService2(resultOfCall1))
.then(mono)
)
.map(Integer::valueOf)
.as(StepVerifier::create)
.expectNext(1)
.verifyComplete();
}
Mono<String> callToService1() {
return Mono.just("1");
}
Mono<Integer> callToService2(String value) {
// parameter that used in a call to service2
return Mono.empty();
}
Not sure why you used publish(Function). Sounds like your requirement would be fulfilled by a simple direct flatMap:
callToService1()
.flatMap(v1 -> callToService2(v1)
.thenReturn(v1)
);
if callToService2 throws or produces an onError, that error will be propagated to the main sequence, terminating it.
(edited below for requirement of emitting value from service1)
otherwise, inside the flatMap the callToService2 is completed then we ignore the result and emit the still in scope v1 value thanks to thenReturn (which also propagates onError if callToService2 emits onError)
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).
Is there any way of catching an Objective-C exception that was thrown as a result of a JavaScript script evaluation?
For example:
I have a class Obj with a method canThrow that I have exported through JSExport. From some script I call this method and it indeed throws an exception.
Any way I can handle it?
I have already tried to wrap the script evaluation code in a try-catch, but that didn't help.
Your question is a little bit unclear, but I will try to answer it anyway.
If you need to raise an exception from Objective-C to Javascript, you should use -[JSContext exception] property. See following question for details.
Passing exception from Javascript to Objective-C is straightforward, you simply export some method, that will handle exception in Objective-C like:
myJSContext[#"PassExceptionToObjC"] = ^void(JSValue *)jsException {
// Handle exception
}
And then use following code in Javascript:
try {
// some code
}
catch (exception) {
PassExceptionToObjC(exception);
}
Alternatively you can return specific value from your Javascript to Objective-C. Like:
function doSomething() {
try {
// Do something
return 'Ok';
}
catch (error) {
return 'Error happens ' + error.message;
}
}
In case you need to avoid throwing Objective-C exception in method, called from Javascript, you simply should add #try/#catch in your Objective-C method or block:
myJSContext[#"DoSomething"] = ^void(JSValue *)someValue {
#try {
// Do something
}
#catch (NSException *exception) {
// Handle exception
}
}
Or, preferred, change logic of Objective-C code, that trigger exception.
Similar way applies if you need to avoid exception in Javascript, called from Objective-C. You simply add try/catch. In most cases you may simply ignore exceptions in JavaScript, called from Objective-C. But I suggest to implement exception handing on Objective-C side for any non-trivial Javascript, at least to simplify troubleshooting.
Hope it helps.
I am using JNA in my project and my Java JNA Callbacks throw exception in some cases. I want to know from C/C++ code an exception was thrown by last calbback method call. In JNI, one can do it using env->ExceptionCheck() but could not find any equivalent in JNA.
Is there any possibility to achieve this?
The native code calling your callback certainly has no expectation that a Java exception will be raised. There is no guarantee that a JNA callback will be invoked from a containing JVM context. Even if it were, you'd have to establish an out of band channel to pass the exception from the callback to the JVM further up the stack, since you have no guarantees about the calling C code.
Assuming you have Java code -> C code -> callback, I'd recommend you catch all your callback's exceptions, then put them somewhere for the calling Java code to examine after the call.
You could make this happen under the covers with an InvocationMapper, which basically lets you capture and/or modify the results of an interface-mapped call, but it's probably easier just to be explicit about it and wrap the whole thing in a utility function.
For example:
public interface MyLibrary extends Library {
MyLibrary INSTANCE = (MyLibrary)Native.loadLibrary();
interface MyCallback extends Callback {
void invoke();
}
void myFunction(MyCallback callback);
}
Then you provide a utility wrapper:
public void myFunction(final MyCallback callback) {
final List<Exception> exceptions = new List<Exception>();
MyLibrary.INSTANCE.myFunction(new MyCallback() {
public void invoke() {
try {
callback.invoke();
} catch(Exception e) {
exceptions.add(e);
}
}
});
if (exceptions.size() > 0) {
// ...
}
}
Assume such conditions:
Some operation does not provide possibility of returning the result.
This operation declared as callback
Using typedef not recommended
Some operation provide of returning the result.
This operation declared as callback
Using typedef not recommended
Assume such scenario:
void main() {
executeVoidOperation(methodNonVoid); // Must throw if method void?
executeNonVoidOperation(methodVoid); // Must throw if method non-void?
}
int methodNonVoid() {
return 0;
}
void methodVoid() {
}
void executeVoidOperation(void operation()) {
operation(); // Must throw if method non-void?
}
void executeNonVoidOperation(dynamic operation()) {
var result = operation(); // Must throw if method void?
print(result); // Result of void operation? (if such passed as argument)
}
Displayed results:
null
Questions (where I wrong?):
Null is object. From where this null appeared (as result) if void function cannot return result (even null)?
Functions with different return types in Dart assumed as the same (not conflicting) types?
How in Dart called this function transformations?
executeNonVoidOperation(methodVoid); works because the callback is defined as dynamic operation(). dynamic can be anything, including void. It's the same as if you just don't specify a type.
The null value stems from a simple rule in Dart. Quoted from the Dart Language Tour:
All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.
That means that every void method always returns null. If you try to return something else, you'll get a runtime error (in checked mode).
executeVoidOperation(methodNonVoid); is a bit more tricky - I'd expect it to throw a runtime error, but it seems the callback is treated as dynamic operation() instead of void operation(). Dart Editor's analyzer seems to think that, too. This may be either a bug or a design choice by the Dart team.