I don't get this behaviour...
main(args) async {
await runZoned(() {
throw false;
}, onError: (e) async {
print("working in onError");
await runZoned(() {
throw false;
}, onError: (e) async {
print("error 1");
});
print("error 2");
});
print("finish");
}
working in onError
error 1
error 2
finish
main(args) async {
await runZoned(() async {
throw await Future.error(false);
}, onError: (e) async {
print("working in onError");
await await runZoned(() async {
throw await Future.error(false);
}, onError: (e) async {
print("error 1");
});
print("error 2");
});
print("finish");
}
working in onError
error 1
those async on runZoned() change the behaviour radically, are they supposed to do so?
I need those asyncs and reaching the print("finish") always. How do I tackle the problem?
You are hitting a sharp edge of futures and error(-handling) zones.
An error thrown in one error zone will not propagate to a future error handler created in another error zone.
So, if you get a future from a different error zone, and it has completed with an error, then you can't get the error, and there is no value to get, so the future will look like it never completes.
The code
main(args) async {
await runZoned(() async {
await Future.error(false);
}, onError: (e) async {
print("working in onError");
await runZoned(() async {
await Future.error(false);
}, onError: (e) async {
print("error 1");
});
print("error 2");
});
print("finish");
}
runs the arguments to runZone in new error zones. They're async functions returning futures, so the the await Future.error(false) will throw and complete the returned future with an error in that particular error zone.
Then, when the runZoned completes, it returns that future with an error created in a different error zone than the call. It is awaited (in the root zone, which is a different error zone) so await runZoned(() async { ... }, ...) never completes. The await waits for the future to complete, the future refuses to give the error that it completed with to the listener, so nothing happens.
You are, effectively, waiting on a future which never completes, which is why your program stops at that point.
So this is working as intended - the error never leaves the error zone it was created in, but that leaves the outer zone without any result.
The first example works because you are throwing synchronously, and not returning a future. The synchronous throw is caught immediately, and then the runZoned returns null. (That will have to change when Dart gets non-nullable types, though).
In general, it's probably a bad idea to return futures that can contain errors from a runZoned with an onError handler. We can't prevent that (returning Object is fine and futures are objects), but it should perhaps be documented.
Thanks to Irn answer I understood and used a Completer for achieving my goal:
main(args) async {
var completer = Completer();
await runZoned(() {
Future.microtask((){
throw Future.error(false);
});
completer.complete();
}, onError: (e) {
// ...
completer.complete();
});
print("finish");
}
Related
I have a facade function that reloads the current firebase user and returns it. The thing is that the user reloading part has a timeout and it needs to be tested.
Function:
Future<Option<User>> getSignedInUser() async {
// Reload currentUser if possible
// it mustn't throw [TimeoutException] for whole function,
// this is what this try/catch does
try {
await reloadCurrentUser().timeout(const Duration(seconds: 20));
} catch (e) {
log(e.toString(), name: TAG);
}
return optionOf(_auth.currentUser);
}
reloadCurrentUser() function:
Future<Either<AuthFailure, Unit>> reloadCurrentUser() async {
try {
await _auth.currentUser?.reload();
return right(unit);
} catch (e) {
log(e.toString(), name: TAG);
return left(const AuthFailure.userReloadingError());
}
}
The question is how to test reloadCurrentUser() timeout? I'm trying to throw a TimeoutException when this function is called, but then it throws an error for the whole test.
Current Test function:
test(
'Reaches timeout when reloading currentUser, '
'throws TimeoutException, but function continues '
'and returns optionOf currentUser', () async {
reset(fakeFirebaseAuth);
reset(fakeFacebookAuth);
reset(fakeGoogleSignIn);
final currentUser = FakeUser();
// It says that currentUser exists and *IS* authenticated
when(() => fakeFirebaseAuth.currentUser).thenReturn(currentUser);
when(() => firebaseAuthFacade.reloadCurrentUser())
.thenThrow(TimeoutException('timeout', const Duration(seconds: 20)));
final result = await firebaseAuthFacade.getSignedInUser();
expect(result, isA<Some<User>>());
});
Maybe it's better to remove timeout and use some connectivity package to ensure that the user has a network connection and only then reload the current user?
For testing I'm using mocktail package.
You can use the fake_async package.
Here's a simple example from their docs that you can modify for your use case:
import 'dart:async';
import 'package:fake_async/fake_async.dart';
import 'package:test/test.dart';
void main() {
test("Future.timeout() throws an error once the timeout is up", () {
// Any code run within [fakeAsync] is run within the context of the
// [FakeAsync] object passed to the callback.
fakeAsync((async) {
// All asynchronous features that rely on timing are automatically
// controlled by [fakeAsync].
expect(Completer().future.timeout(Duration(seconds: 5)),
throwsA(isA<TimeoutException>()));
// This will cause the timeout above to fire immediately, without waiting
// 5 seconds of real time.
async.elapse(Duration(seconds: 5));
});
});
}
So, I have learned how to create and update documents in a firebase firestore cloud, however I am having trouble reading data. Attached is my code for finding the value of the 'photourl' field:
String photoy;
Firestore.instance.collection('userdata').document('sepDGexTRuRkpx1WQYylDDmUI573')
.get().then((DocumentSnapshot ds){
photoy=ds.data['photourl'];
});
setState(() {
photourldisplay=photoy;
});
However, upon running my program, the photourldisplay value seems to not have changed and remains null upon running. This means that something is askew with my code that retrieves this "photourl" field. Can someone help me retrieve a field in a firebase document?
photoy does not contain the value you expect because Firestore queries are asynchronous. The get() returns immediately, and the callback is invoked some time later, after the query completes. There is no guarantee how long a query might take. If you want to pass the value of photoy to something else, you will have to wait until the callback completes by making use of it only within that callback.
Firestore.instance.collection('userdata').document('sepDGexTRuRkpx1WQYylDDmUI573')
.get().then((DocumentSnapshot ds){
photoy=ds.data['photourl'];
setState(() {
photourldisplay=photoy;
});
});
Your code is good you just have to await for the result:
void yourVoid () async {
String photoy;
await Firestore.instance.collection('userdata').document('sepDGexTRuRkpx1WQYylDDmUI573')
.get().then((DocumentSnapshot ds){
photoy=ds.data['photourl'];
});
setState(() {
photourldisplay=photoy;
});
}
EDIT:
as #Doug Stevenson said, there is two propers solutions:
void yourVoid () async {
DocumentSnapshot ds = await Firestore.instance.collection('userdata').document('sepDGexTRuRkpx1WQYylDDmUI573')
.get();
String photoy = ds.data['photourl'];
setState(() {
photourldisplay=photoy;
});
}
and:
Firestore.instance.collection('userdata').document('sepDGexTRuRkpx1WQYylDDmUI573')
.get().then((DocumentSnapshot ds){
photoy=ds.data['photourl'];
setState(() {
photourldisplay=photoy;
});
});
I'd like to understand when to use throw and return Future.error in async functions. Are there any underlying differences that we should take in consideration when choosing one or the other?
I did a small test:
Future testFuture() async {
return Future.error('error from future');
}
void testThrow() {
throw('error from throw');
}
Future testFutureThrow() async {
throw('error from future throw');
}
main() async {
print('will run test');
try{
await testFuture();
}
catch(e){
print(e);
}
try{
testThrow();
}
catch(e){
print(e);
}
try{
await testFutureThrow();
}
catch(e){
print(e);
}
testFuture().catchError((e) => print(e));
testFutureThrow().catchError((e) => print(e));
print('did run test');
}
They both seem to work in the same way. This is the output:
will run test
error from future
error from throw
error from future throw
did run test
error from future
error from future throw
What we can see from this test is that, from the point of view of the caller, when call the function using try/catch the code runs synchronous and if we Future.catchError it's asynchronous. But from the point of view of the function flow it appears to be the same.
NOTE: I had this initially as a response to a question. But then I realised that I was not answering but instead doing a question.
async and await are a language extension/syntactic sugar to simplify working with asynchronous code by restoring a synchronous fashion.
Both abstract away working with Futures directly, making this code analogous:
// On the callee's side
Future<String> calleeAsync() async {
return "from async callee";
}
Future<String> calleeSync() {
return Future.value("from sync callee");
}
// On the caller's side
Future<void> callerAsync() async {
print("got ${await calleeAsync()}");
print("got ${await calleeSync()}");
}
void callerSync() {
calleeAsync()
.then((v) => print("got3 $v"))
.then((_) => calleeSync())
.then((v) => print("got4 $v"));
}
(while in callerSync the second then()'s callback could be merged with the first's, and the third then() could also be attached directly to calleeSync() because of monad-like associativity)
In the same manner, these functions are equivalent:
// On the callee's side
Future<void> calleeAsync() async {
throw "error from async";
}
Future<void> calleeSync() {
return Future.error("error from sync");
}
// On the caller's side
Future<void> callerAsync() async {
try {
await calleeAsync();
} catch (e) {
print("caught $e");
}
try {
await calleeSync();
} catch (e) {
print("caught $e");
}
}
void callerSync() {
calleeAsync()
.catchError((e) => print("caught $e"))
.then((_) => calleeSync())
.catchError((e) => print("caught $e"));
}
In summary, there is no practical difference. return Future.error(…) simply doesn't make use of the extensions an async function provides, thus it's more like mixing two flavours of asynchronous code.
My experience is that if you want to use myFunc().then(processValue).catchError(handleError); you should return Future.error but not throw it.
How to make a await future not last more than 5 seconds ?
I need it because in some networking operation, the connection is sometimes producing silent error. Hence my client just wait for hours with no response. Instead, I want it trigger an error when the clients waits for more than 5 seconds
My code can trigger the error but it is still waiting
Future shouldnotlastmorethan5sec() async {
Future foo = Future.delayed(const Duration(seconds: 10));;
foo.timeout(Duration(seconds: 5), onTimeout: (){
//cancel future ??
throw ('Timeout');
});
await foo;
}
Future test() async {
try{
await shouldnotlastmorethan5sec(); //this shoud not last more than 5 seconds
}catch (e){
print ('the error is ${e.toString()}');
}
}
test();
When you call Future.timeout you need to use the return value to get the correct behaviour. In your case:
Future shouldnotlastmorethan5sec() {
Future foo = Future.delayed(const Duration(seconds: 10));
return foo.timeout(Duration(seconds: 5), onTimeout: (){
//cancel future ??
throw ('Timeout');
});
}
Ok so I am newbie in Angularjs,one of my current task is to code a CRUD functionality and I used the Promise to handle it.
dao.updateEntityCharSpecUseRelSql = function (paramField) {
return new Promise(function (resolve, reject) {
.......
}).catch(err => { reject(err)});// **my PM says, catching error is wrong**
}
module.exports = dao;
First, I thought, catch block is just alright because I am getting it from the Promise object which returns the error if something goes wrong.
But my Pm says, in order to use this, promises should have .then() first.
Is it really a bad practice to use the catch() without then(). What he propose is that instead, I should create a try & catch block inside the Promise() something like
new Promise (function(resolve, reject){
try {
resolve(something)
}catch(err){
reject(err)
}
})
Please enlighten me for this.TIA
Let's do a step back.
When you create a new Promise() you are responsible to handle each success and failure case.
Who is calling your function (and getting your promise) is responsible to handle the response inside a then() callback, if the promise was successfully resolved, or a catch() callback, if the promise was rejected.
So, you don't need to catch your own promise.
Example:
function init() {
getAsyncValues()
.then(function (result) {
// it will do something based on his business logic
})
.catch(function (error) {
// it will do something based on his business logic
});
}
function getAsyncValues() {
return new Promise(function(resolve, reject) {
...
resolve(SOME_VALUES);
...
reject(SOME_ERRORS);
})
}
And if your "promise handler" need to call async task before resolving something, you can wait his resolution like:
const examplePromise = new Promise(fuction (resolve, reject) {
asyncTask() // executing an async task
.then(function(result) {
resolve(result);
})
.catch(function(error) {
reject(error)
});
})