So I'm experimenting with a try/catch clause, and I don't understand why this is happening (normal or not):
void main() {
List someList = [1,2,3];
try {
for (var x in someList) {
try {
for (var z in x) {
}
} catch(e) {
throw new Exception('inside');
}
}
} catch(e) {
throw new Exception('outside');
}
}
So you see I'm trying to do a loop inside a loop, but on purpose, someList is not a List<List>, therefore the nested loop will throw an error ('inside' error) since 1 is an int, not a List.
That's the scenario, but what happens is it throws the 'outside' error.
Is this normal? If so, where did I go wrong?
The exception you're getting is because w is undefined. If you change w to someList, then you'll get an exception for x not having an iterator, like you expect. You'll then handle that "inside" exception and immediately throw another one, which you'll catch, and then you'll throw the "outside" one, which you don't handle and will see an error for. (This might make it look like you're getting an "outside" error, but the error started on the "inside".)
This might make things clearer:
void main() {
List someList = [1,2,3];
try {
for (var x in someList) {
try {
for (var z in x) { // This throws an exception
}
} catch(e) { // Which you catch here
print(e);
print("Throwing from inside");
throw new Exception('inside'); // Now you throw another exception
}
}
} catch(e) { // Which you catch here
print(e);
print("Throwing from outside");
throw new Exception('outside'); // Now you throw a third exception
}
} // Which isn't caught, causing an
// "Unhandled exception" message
Related
I am working with callBack flow to observe a Firestore document. My flow needs to throw ResourceNotFoundException when the document being watched gets moved or deleted by some other person using the app. Below is my code for the flow
fun observeDocument(collectionId: String, documentId: String) = callbackFlow {
database.collection(collectionId).document(documentId)
.addSnapshotListener { documentSnapshot, firebaseFireStoreException ->
if (firebaseFireStoreException != null)
throw firebaseFireStoreException
if (documentSnapshot == null || !documentSnapshot.exists()) {
throw ResourceNotFoundException("")
}
try {
offer(documentSnapshot.toObject(PrintOrder::class.java))
} catch (e: Exception) {
}
}
awaitClose { }
}
and I am collecting the above flow in the ViewModel by using the following code
viewModelScope.launch {
repository.observeDocument(collectionId, documentId)
.catch {e->
_loadedPrintOrder.value = LoadingStatus.Error(e)
}
.onStart {
_loadedPrintOrder.value = LoadingStatus.Loading(application.getString(R.string.one_moment_please))
}
.collect {
_loadedPrintOrder.value = LoadingStatus.Success(it!!)
}
}
Now, the problem is catch operator is not catching the exception thrown from the flow (ResourceNotFoundException). I have even tried wrapping the flow collection code inside a try-catch block. Even the try-catch block fails to catch the exception and the app crashes.
How can I catch the Exception which is thrown from the callBackFlow during collection of the flow?
The catch operator catches exceptions in the flow completion. It will catch only if the flow completes normally or exceptionally. As you saw, it will never be thrown inside a flow builder. Moreover, your flow never completes since you are using awaitClose to keep him alive.
Since this builder uses a Channel under the hood, you can close it manually with non-null cause in order to complete the flow exceptionally:
abstract fun close(cause: Throwable? = null): Boolean
So you can use a try/catch inside the callbackFlow builder to close it manually as follows:
database.collection(collectionId)
.document(documentId)
.addSnapshotListener { documentSnapshot, firebaseFireStoreException ->
try {
if (firebaseFireStoreException != null)
throw firebaseFireStoreException
if (documentSnapshot == null || !documentSnapshot.exists()) {
throw ResourceNotFoundException("Oops!")
}
offer(documentSnapshot.toObject(PrintOrder::class.java))
} catch (e: Exception) {
close(e) // close the flow with a non-null cause
}
}
With this, you should be able to catch exceptions as your firebaseFireStoreException, ResourceNotFoundException or any other one inside the catch operator because the flow is now completed exceptionally.
Im coming from a Java background where I use the throws keyword to lead an exception to the method calling another method. How can I do that I dart?
Method called:
void _updateCurrentUserEmail() async {
await FirebaseAuth.instance
.currentUser()
.then((FirebaseUser user) {
_email = user.email;
});
}
How it is called:
try {
_updateCurrentUserEmail();
} on Exception {
return errorScreen("No User Signed In!", barActions);
}
But it seems like the Exception is not caught, because I still get a NoSuchMethodException and the errorScreen is not shown.
While you correctly used try/catch, the exception is coming from an async function that you did not await.
try/catch only catch exceptions thrown within that block. But since you wrote:
try {
doSomethingAsyncThatWillTrowLater();
} catch (e) {
}
Then the exception thrown by the async method is thrown outside of the body of try (as try finished before the async function did), and therefore not caught.
Your solution is to either use await:
try {
await doSomethingAsyncThatWillTrowLater();
} catch (e) {
}
Or use Future.catchError/Future.then:
doSomethingAsyncThatWillTrowLater().catchError((error) {
print('Error: $error');
});
From the docs,
If the catch clause does not specify a type, that clause can handle any type of thrown object:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e'); <------------------
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
Change it to this:
try {
_updateCurrentUserEmail();
} on Exception catch(e){
print('error caught: $e')
}
Another way to handle error is to do the following:
void _updateCurrentUserEmail() async {
await FirebaseAuth.instance
.currentUser()
.then((FirebaseUser user) {
_email = user.email;
throw("some arbitrary error");
});
.catchError(handleError);
}
handleError(e) {
print('Error: ${e.toString()}');
}
If currentUser()’s Future completes with a value, then()’s callback fires. If code within then()’s callback throws (as it does in the example above), then()’s Future completes with an error. That error is handled by catchError().
Check the docs:
https://dart.dev/guides/libraries/futures-error-handling
Throw
Here’s an example of throwing, or raising, an exception:
throw FormatException('Expected at least 1 section');
You can also throw arbitrary objects:
throw 'Out of llamas!';
throwing an exception is an expression, you can throw exceptions in => statements, as well as anywhere else that allows expressions:
void someMethod(Point other) => throw UnimplementedError();
here is example
main() {
try {
test_age(-2);
}
catch(e) {
print('Age cannot be negative');
}
}
void test_age(int age) {
if(age<0) {
throw new FormatException();
}
}
hope it helps..
In my Flutter app I'd like to make multiple network calls simultaneously and then do something when they all have finished. For this I use Future.wait(), which does what I want. However when a call fails it throws an exception, which is somehow not caught in the exception handler (i.e. uncaught exception).
When I do await _fetchSomeData() separately (outside Future.wait()) the exception does get called by the exception handler as expected.
Future<bool> someMethod() async {
try {
var results = await Future.wait([
_fetchSomeData(),
_fetchSomeOtherData()
]);
//do some stuf when both have finished...
return true;
}
on Exception catch(e) {
//does not get triggered somehow...
_handleError(e);
return false;
}
}
What do I need to do to catch the exceptions while using Future.wait()?
Update:
I have narrowed down the issue. Turns out if you use another await statement in the method that is called by the Future.wait() it causes the issue. Here an example:
void _futureWaitTest() async {
try {
//await _someMethod(); //using this does not cause an uncaught exception, but the line below does
await Future.wait([ _someMethod(), ]);
}
on Exception catch(e) {
print(e);
}
}
Future<bool> _someMethod() async {
await Future.delayed(Duration(seconds: 0), () => print('wait')); //removing this prevents the uncaught exception
throw Exception('some exception');
}
So if you either remove the await line from _someMethod() or if you just call _someMethod() outside of Future.wait() will prevent the uncaught exception. This is most unfortunate of course, I need await for an http call... some bug in Dart?
I have the Uncaught Exceptions breakpoints enabled. If I turn this off the issue seems to be gone. Perhaps it's an issue with the debugger. I am using Visual Studio Code and the latest flutter.
What do I need to do to catch the exceptions while using Future.wait()?
What I found out when I used the same code as you the code inside of each procedure which is used in Future.wait() must be wrapped with try/catch and on catch must return Future.error(). Also eagerError must be set to true.
try {
await Future.wait([proc1, ...], eagerError: true);
} on catch(e) {
print('error: $e')
}
/// Proc 1
Future<void> proc1() async {
try {
final result = await func();
} on SomeException catch(e) {
return Future.error('proc 1 error: $');
}
}
I think you are a bit mislead by the Future.wait() naming. Future.wait() returns another future that will have a List of elements returned by each future when it completes with success.
Now since the Future.wait() is still a future. You can handle it in two ways:
Using await with try catch.
Using onError callback.
Tis will be something like
Future.wait([futureOne, futureTwo])
.then((listOfValues) {
print("ALL GOOD")
},
onError: (error) { print("Something is not ok") }
I have a HTML5 worker which sends values with postMessage. Sometimes (for example if the result is a function) the code throws an exception:
DataCloneError: The object could not be cloned.
So I tried to catch the exception:
try {
self.postMessage (result);
}
catch (ex) {
if (ex instanceof DataCloneError)
self.postMessage (result.toString());
else
throw ex;
}
But this throws the following exception:
ReferenceError: DataCloneError is not defined
I am confused. How to catch the DataCloneError?
The error you receive is an instance of the DOMException interface.
To know which DOMException it is, you can check its name property.
The one of DATA_CLONE_ERROR, is "DataCloneError".
try {
postMessage( () => {} , '*' );
}
catch( err ) {
console.log( err.name === "DataCloneError" );
}
I recently stumbled across the following Dart code:
void doSomething(String url, String method) {
HttpRequest request = new HttpRequest();
request.open(method, url);
request.onLoad.listen((event) {
if(request.status < 400) {
try {
String json = request.responseText;
} catch(e) {
print("Error!");
}
} else {
print("Error! (400+)");
}
});
request.setRequestHeader("Accept", ApplicationJSON);
}
I'm wondering what the e variable is in the catch clause:
catch(e) {
...
}
Obviously its some sort of exception, but (1) why do we not need to specify its type, and (2) what could I add in there to specify its concrete type? For instance, how could I handle multiple types of possible exceptions in a similar way to catchError(someHandler, test: (e) => e is SomeException)?
Dart is an optional typed language. So the type of e is not required.
you have to use the following syntax to catch only SomeException :
try {
// ...
} on SomeException catch(e) {
//Handle exception of type SomeException
} catch(e) {
//Handle all other exceptions
}
See catch section of Dart: Up and Running.
Finally catch can accept 2 parameters ( catch(e, s) ) where the second parameter is the StackTrace.