Why can't catch exceptions with "catchError" in Dart? - dart

Test:
void testAs() async {
try {
String b = await test();
print(b);
} catch (e) {
print("1 await error");
}
test().then((value) => print(value)).catchError(() {
print("2 then error");
});
}
Future<String> test() {
List<String> bb = ["2222"];
return Future.value(bb[1]);
}
1 await error
RangeError (index): Invalid value: Only valid value is 0: 1
Why is it ineffective?
If I want to deal with "future" through "then", how should I catch the exception and not let it throw out.

Thank you friends,the last problem has been solved,the problem can be solved by adding async and await flags to the test() method.
But there is a new problem, Now I use the correct code and find that it can only be printed once.why can't it print "then success",then the program ends
,modify as follows:
void testAs() async {
try {
await test();
print("await success");
} catch (e) {
print("await error");
}
test().then((value) => print("then success")).catchError((e) {
print("then error");
});
}
Future<String> test() async{
List<String> bb = ["2222"];
return await Future.value(bb[0]);
}
print:await success

Related

Dart : Make reusable try catch block for error handling

I have simple function login request to server, in this function i have some error handling .
Example Source Code
try {
final response = await http.post(
'${appConfig.baseApiUrl}/${appConfig.userController}/loginUser',
headers: appConfig.headersApi,
body: {
"username": username,
"password": password,
},
);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson["status"] == "ok") {
List userList = responseJson['data'];
List<UserModel> result = userList.map((e) => UserModel.fromJson(e)).toList();
return result;
} else {
throw CustomError(responseJson['message']);
}
} on SocketException catch (_) {
return Future.error(ConstText.NO_CONNECTION);
} on TimeoutException catch (_) {
return Future.error(ConstText.TIMEOUT_EXCEPTION);
} on FormatException catch (_) {
return Future.error(ConstText.FORMAT_EXCEPTION);
} catch (e) {
return Future.error(e.toString());
}
}
In above source code, I have 4 Handling error like SocketException,TimeoutException,FormatException and UnknownException. This function work fine, but if i create another function for request server i should repeat the error handling again.
My question is , it's possible to make error handling reusable ? I want something like this.
Example Reusable Try Catch
requestServer(yourRequestServer) async{
try{
return yourRequestServer;
}on SocketException catch (_) {
return Future.error(ConstText.NO_CONNECTION);
} on TimeoutException catch (_) {
return Future.error(ConstText.TIMEOUT_EXCEPTION);
} on FormatException catch (_) {
return Future.error(ConstText.FORMAT_EXCEPTION);
} catch (e) {
return Future.error(e.toString());
}
}
How To Use it
Future<String> testLogin(String username,String password)async{
requestServer({
final response = await http.post(
'${appConfig.baseApiUrl}/${appConfig.userController}/loginUser',
headers: appConfig.headersApi,
body: {
"username": username,
"password": password,
},
);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson["status"] == "ok") {
List userList = responseJson['data'];
List<UserModel> result = userList.map((e) => UserModel.fromJson(e)).toList();
return result;
} else {
throw CustomError(responseJson['message']);
}
});
}
Or if you have another suggestion , i really appreciate that.
Thank's.
Reusing code is always a matter of figuring out what to keep an what to abstract away.
In your case, you want to reuse the catch clauses.
The thing you are abstracting over is the body of the try clause, which appears to contain some asynchronous code.
Since you are abstracting over code, you'll need to pass in a function. That's the only way to make code into an argument value.
So you'll need something like:
Future<T> requestServer(FutureOr<T> computation()) {
try {
return await computation();
} on SocketException catch (_) {
throw ConstText.NO_CONNECTION;
} on TimeoutException catch (_) {
throw ConstText.TIMEOUT_EXCEPTION;
} on FormatException catch (_) {
throw ConstText.FORMAT_EXCEPTION;
} catch (e) {
throw e.toString();
}
}
You can then use it as:
var result = await requestServer(() {
final response = await http.post(...
...
return result;
...
});
I changed the return Future.error(someString); to throw someString because it's the same thing, and the latter is much more readable. You are throwing strings here, not exception or error objects. That's a bold move, but as long as you are the one to catch them again, it's reasonable. It's not a good API for other people to have to catch.

Flutter dart function skipping to end of function after await

I have a function with the keyword async and after the await is done, it skips a bunch of lines of codes and goes to the end of the function after the await is done. Does anyone know what I am doing wrong?
void _createNewGroup(Function createNewGroup, BuildContext context) async {
if (_validateAndSave()) {
try {
print("trying");
Map<String, dynamic> result = await createNewGroup(_group_name);
print("sucess is $result");
if (result['success']) {
print('Success poping dialoge');
Navigator.of(context).pop();
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => HomePage()));
} else {
print("not created group");
}
} catch (error) {}
}
}
Future<Map<String, dynamic>> createNewGroup(String name) async {
var result = {};
var response;
print('Group name $name');
print("creating group");
var group = ParseObject('Groups')
..set("group_name", name)
..set('createdBy_name', _parseUser.get('first_name'))
..set('CreatedBy_id', _parseUser.objectId)
..set('members', [{'name': _parseUser.get('first_name'), 'id': _parseUser.objectId}]);
response = await group.save();
if(response.success) {
print('sucess creating object');
result = {'success': true, 'message': 'New Group Created'};
return result;
} else {
result = {'success': false, 'message': 'Error occured creating group'};
}
print("result is $result");
print("response is ${response.result}");
return result;
}

future functions keep repeating

am trying to read and write in files in my flutter app .. like this:
Future<String> get _localPath async {
print('hi');
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
File f = File('$path/mypollshash.txt');
if (f.existsSync()) {
print('exists');
String contents = await f.readAsString();
content = contents;
fetchHash();
} else {
print('not exists');
fetch();
}
return f;
}
Future checkfileexist() async {
try {
final file = await _localFile;
String contents = await file.readAsString();
content = contents;
} catch (e) {
//return 'nothing';
}
}
Future<File> writehash(String hash) async {
final file = await _localFile;
return file.writeAsString('$hash', mode: FileMode.write);
}
Future<File> get _localjson async {
final path = await _localPath;
return File('$path/mypolls.json');
}
Future<File> writejson(String json) async {
final file = await _localjson;
return file.writeAsString('$json', mode: FileMode.write);
}
readjson() async {
try {
final file = await _localjson;
String contents = await file.readAsString();
content = contents;
setState(() {
polls = pollsFromJson(content);
isloading = false;
});
writejson(pollsToJson(polls));
writehash(polls.hash);
print('here');
// return contents;
} catch (e) {
fetch();
print('there');
print(e);
// If we encounter an error, return 0
//return 'nothing';
}
}
fetch() async {
String data =
await DefaultAssetBundle.of(context).loadString("assets/mypolls.json");
setState(() {
polls = pollsFromJson(data);
isloading = false;
});
writejson(pollsToJson(polls));
writehash(polls.hash);
}
fetchHash() async {
String data = await DefaultAssetBundle.of(context)
.loadString("assets/pollshash.json");
print(content);
final pollshash = pollshashFromJson(data);
if (content == pollshash.hash) {
print('take from the saved json');
readjson();
} else {
print('call api');
fetch();
}
}
and then am calling it here:
#override
void initState() {
super.initState();
checkfileexist();
}
this works fine .. but the method will keep called even when i go to another page and will get this printed over and over again:
I/flutter (17060): hi
I/flutter (17060): here
I/flutter (17060): exists
I/flutter (17060): d1f4bd60f52991d100adafa416f48b52
I/flutter (17060): take from the saved json
I want this to be called only once .. how to do this?
InitState is not called once it's normal. Instead you can do it with multiple solutions.
Create an attributes in your component to memorize if you already did your checks like this
class MyComponentState ... {
bool hasChecked = false;
bool isFileExists = false;
#override
initState() {
super.initState();
if(!hasChecked) {
this.hasChecked = true;
this.isFileExists = checkfileexist();
}
}
}

Dart get back value of function

I'm trying to learn Dart by my self, but I come from C and I a bit confused...
I'm doing this :
import 'dart:io';
import 'dart:async';
import 'dart:convert';
Future <Map> ft_get_data()
{
File data;
data = new File("data.json");
return data.exists().then((value) {
if (!value)
{
print("Data does no exist...\nCreating file...");
data.createSync();
print("Filling it...");
data.openWrite().write('{"index":{"content":"Helllo"}}');
print("Operation finish");
}
return (1);
}).then((value) {
data.readAsString().then((content){
return JSON.decode(content);
}).catchError((e) {
print("error");
return (new Map());
});
});
}
void main()
{
HttpServer.bind('127.0.0.1', 8080).then((server) {
print("Server is lauching... $server");
server.listen((HttpRequest request) {
request.response.statusCode = HttpStatus.ACCEPTED;
ft_get_data().then((data_map) {
if (data_map && data_map.isNotEmpty)
request.response.write(data_map['index']['content']);
else
request.response.write('Not work');
}).whenComplete(request.response.close);
});
}) .catchError((error) {
print("An error : $error.");
});
}
I'm trying to get back the new Map, and as you can guess, it doesn't work and I get the 'Not work' msg. While when the code was in same function, it worked...
Please, could you help me ?
And, there a pointer system as C ?
void function(int *i)
{
*i = 2;
}
int main()
{
int i = 1;
function(&i);
printf("%d", i);
}
// Output is 2.
Thank you for your help.
Final code :
import 'dart:io';
import 'dart:async';
import 'dart:convert';
Future<Map> ft_get_data()
{
File data;
data = new File("data.json");
return data.exists()
.then((value) {
if (!value) {
print("Data does no exist...\nCreating file...");
data.createSync();
print("Filling it...");
data.openWrite().write('{"index":{"content":"Helllo"}}');
print("Operation finish");
}
})
.then((_) => data.readAsString())
.then((content) => JSON.decode(content))
.catchError((e) => new Map());
}
void main()
{
HttpServer.bind('127.0.0.1', 8080)
.then((server) {
print("Server is lauching... $server");
server.listen((HttpRequest request) {
request.response.statusCode = HttpStatus.ACCEPTED;
ft_get_data()
.then((data_map) {
if (data_map.isNotEmpty)
request.response.write(data_map['index']['content']);
else
request.response.write('Not work');
})
.whenComplete(request.response.close);
});
})
.catchError((error) {
print("An error : $error.");
});
}
I tried to reconstruct your code to "readable" format. I haven't test it, so there might be errors. For me the code is much easier to read if .then() are not nested. Also it helps reading, if .then() starts a new line.
import 'dart:io';
import 'dart:async';
import 'dart:convert';
Future <Map>ft_get_data()
{
File data;
data = new File("data.json");
data.exists() //returns true or false
.then((value) { // value is true or false
if (!value) {
print("Data does no exist...\nCreating file...");
data.createSync();
print("Filling it...");
data.openWrite().write('{"index":{"content":"Helllo"}}');
print("Operation finish");
}
}) // this doesn't need to return anything
.then((_) => data.readAsString()) // '_' indicates that there is no input value, returns a string. This line can removed if you add return data.readAsString(); to the last line of previous function.
.then((content) => JSON.decode(content)); // returns decoded string, this is the output of ft_get_data()-function
// .catchError((e) { //I believe that these errors will show in main-function's error
// print("error");
// });
}
void main()
{
HttpServer.bind('127.0.0.1', 8080)
.then((server) {
print("Server is lauching... $server");
server.listen((HttpRequest request) {
request.response.statusCode = HttpStatus.ACCEPTED;
ft_get_data()
.then((data_map) {
if (data_map && data_map.isNotEmpty)
request.response.write(data_map['index']['content']);
else
request.response.write('Not work');
})
.whenComplete(request.response.close);
});
})
.catchError((error) {
print("An error : $error.");
});
}
you cannot insert one then() into the other. Need to chain them. Otherwise, return JSON.decode(data) returns to nowhere (main event loop) instead of previous "then" handler
After a brief look I would say you need
Future<Map> ft_get_data() {
...
return data.exists() ...
...
}
and use it like
server.listen((HttpRequest request) {
request.response.statusCode = HttpStatus.ACCEPTED;
ft_get_data().then((data_map) {
if (data_map && data_map.isNotEmpty) request.response.write(
data_map['index']['content']);
else
request.response.write('Not work');
request.response.close();
});
});
A return inside a then doesn't return from ft_get_data but only from then
If an async call is involved you can't continue if it was sync, it's then async all the way down.

q.js automatically propagate errors (catch async thrown errors)?

i would like to know if there's any way to automatically propagate errors from a promise to another? IE: catch the thrown error from a nested promise.
for example, in the following code sample, the "internalWorker" nested promise function needs
.fail(function (error) {
return deferred.reject(error);
});
in order to propagate the error. if this line isn't contained, the error is throw to the top. (crashed app)
would it be possible to automatically propagate the error so i don't need to add .fail() functions to all my nested promises?
```
function top(input) {
var deferred = q.defer();
internalWorker(input).then(function (value) {
logger.inspectDebug("top success", value);
}).fail(function (error) {
return deferred.reject(error);
});
return deferred.promise;
}
function internalWorker(input) {
var deferred = q.defer();
q.delay(100).then(function () {
throw new Error("internal worker async error");
}).fail(function (error) {
return deferred.reject(error);
});
return deferred.promise;
}
top("hello").then(function (value) {
logger.inspectDebug("outside success", value);
}).fail(function (error) {
logger.inspectDebug("outside fail", error);
}).done();
```
If you are using https://github.com/kriskowal/q, this will do what you intend:
function top(input) {
return internalWorker(input).then(function (value) {
logger.inspectDebug("top success", value);
return value;
});
}
function internalWorker(input) {
return q.delay(100).then(function () {
throw new Error("internal worker async error");
return value;
});
}
top("hello").then(function (value) {
logger.inspectDebug("outside success", value);
}, function (error) {
logger.inspectDebug("outside fail", error);
}).done();
Return promises or values from within callbacks. Errors propagate implicitly.

Resources