I'm retrieving some data from FireBase but the following code returns null
var taxes = new List();
Future.wait(providerTax.map((e) async {
var details;
await FirebaseFirestore.instance
.doc(e['tax'].path)
.get()
.then((value) => details = value.data());
taxes.add({
'taxID': e['identifier'],
'taxName': details['taxID'],
'taxRate': details['unitText'],
'taxAmount': store.state.numberWorkedHours *
offer.data()['price'] *
details['value']
});
}));
The same code called differently works and returns the expected result
List tempTax = new List();
for (var tax in providerTax) {
var details;
await FirebaseFirestore.instance
.doc(tax['tax'].path)
.get()
.then((value) => details = value.data());
tempTax.add({
'taxID': tax['identifier'],
'taxName': details['taxID'],
'taxRate': details['unitText'],
'taxAmount': store.state.numberWorkedHours *
offer.data()['price'] *
details['value']
});
Is this related to an incorrect call for futures to execute or is this related to the fact that .map executes lazily?
Related
In my Flutter Web application I am retrieving values from the map timeslots in Firestore.
This is what the data looks like:
But, instead of retrieving the whole list of values, I get a truncated list like this:
[Mo-Washing-(09:00-10:00, 10:00-11:00, 11:00-12:00, ..., 20:00-21:00, 21:00-22:00)]
Below I have included the 2 functions responsible for retrieving the data and adding it to the list object
static List object = [];
static Map<String, dynamic> timeDetails = {};
static Map<String, dynamic> userDetails = {};
checkExists(docuID) async {
return await firestore()
.collection('environments')
.doc(docuID)
.get()
.then((val) {
userDetails.addAll(val.data());
}).whenComplete(() async {
fs.DocumentSnapshot snapShot = await firestore()
.collection('environments')
.doc(docuID)
.collection('Washing')
.doc('monday')
.get();
if (snapShot == null || !snapShot.exists) {
print('does not exist');
} else {
await getData(docuID, 'Washing');
}
setState(() {});
});
}
getData(docuID, machineName) async {
return await firestore()
.collection('environments')
.doc(docuID)
.collection(machineName)
.doc('monday')
.get()
.then((val) {
timeDetails.addAll(val.data());
}).whenComplete(() {
object.add('Mo-$machineName-${timeDetails['timeslots'].values}');
print(object);
setState(() {});
});
}
This also happens in debugPrint. Would anyone know why this is happening and how I could solve it? Any help on this would be appreciated!
Neither the workaround as mentioned on Github nor debugPrint worked for me, but I managed to solve this by adding .toList() to my getData function:
getData(docuID, machineName) async {
return await firestore()
.collection('environments')
.doc(docuID)
.collection(machineName)
.doc('monday')
.get()
.then((val) {
timeDetails.addAll(val.data());
}).whenComplete(() {
//toList() is added here to .add
object.add('Mo-$machineName-${timeDetails['timeslots'].values.toList()}');
print(object);
setState(() {});
});
}
Output:
[Mo-Washing-[09:00-10:00, 10:00-11:00, 11:00-12:00, 12:00-13:00, 13:00-14:00, 14:00-15:00, 15:00-16:00, 16:00-17:00, 17:00-18:00, 18:00-19:00, 19:00-20:00, 20:00-21:00, 21:00-22:00]
I store all API data to cache. some APIs have more than 10000 data. Postman response time is within one second. but in application very slow to navigate to next page. I used this code:
onPressed: () async {
...
}
else {
var token = Token(
id: 1,
token: tokens,
refreshToken: model.data.refreshToken,
);
await storeRegister(_url,tokens);
await storeEquipmentReg(_url,tokens);
await storeSyncLogin(_url,tokens);
await HelperDefCatMaster().deleteDefCatMaster();
await storeDefCatMaster(_url,tokens);
await HelperDefRegisterCat().deleteDefRegisterCat();
await storeDefRegisterCat(_url,tokens);
await HelperDefCatMaster().deleteDefCatRelation();
await storeDefCatRelation(_url,tokens);
await HelperDefCatMaster().deleteWoDescription();
await storeWoDescription(_url,tokens);
await HelperDefCatMaster().deleteCategoryDefect();
await storeCategoryDefect(_url,tokens);
await storeWorkSource(_url,tokens);
await storeWorkTypes(_url,tokens);
await storePriorities(_url,tokens);
await Helper().insert(token);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ListPage(model.data.token)));
}
storePriorities function look like,
storePriorities(String url, String token) async {
final response = await http.get(
'${url}/v1.0/Priorities',
headers: {'Authorization': 'Bearer ${token}'},
);
final jsonResponse = json.decode(response.body);
Priorities model = Priorities.fromJson(jsonResponse);
int length = model.data.length;
for (int i = 0; i < length; i++) {
var data = DataPriorities(
i: model.data[i].i,
d: model.data[i].d,
);
await HelperDefCatMaster().insertPriorities(data);
}
}
I have given the first answer that suggests to use await only when it's needed.
Well if you are inserting too much data in SQLite I assume that you might be using something like this:
for (int i = 0; i <= 1000; i++) {
db.insert('table_name', dataObject.toMap());
}
Well this will do a lot many transactions at a time and it will consume a lot's of your time.
Change this to something like this and it will increase the speed of inserting data:
Batch batch = db.batch();
for (int i = 0; i <= 1000; i++) {
batch.insert('table_name', dataObject.toMap());
}
await batch.commit();
What we are doing here is that, in single transaction we are doing multiple inserts at a time.
I made this change in my demo project where I was inserting 1000 row at a time and results were great. db.insert when called 1000 times took 7 secs where as batch.insert took less than 1 sec for inserting same amount of data.
If you optimize your code with this solution and use await when needed you should not face any problem on UI. Let me know if this helps.
You are using await keyword to fetch data from SQLite.
And you are fetching a lots of data.
This will make data fetching synchronous, and will affect your UI.
If it is convenient for your use-case to fetch data asynchronously then you can use the following way:
Change :
await Helper().insert(token);
Navigator.push(
context,MaterialPageRoute(builder: (context) => ListPage(model.data.token)));
to :
Helper().insert(token).then((onValue) {
Navigator.push(context,MaterialPageRoute(
builder: (context) => ListPage(model.data.token),
),
);
}
Note: Make your insert method return Future<'token's return type'>
Now use this way for all other await calls.
In my Electron App I would like to inject data (like Fixtures) when App launched.
I use typeorm library for managing my SQLite3 Database connection.
I created json file that represent Entity typeorm and I would like persist all of them in my DB with typeorm. For that It seems that use trasaction is more efficient.
I try two differents things but the result is the same and I don't uderstand why. The issue message is :
Error: Transaction already started for the given connection, commit
current transaction before starting a new one
My first implementation of transaction :
async setAll(entity, data)
{
let connection = await this.init()
const queryRunner = connection.createQueryRunner()
await queryRunner.connect()
for (const [key, value] of Object.entries(data))
{
await typeorm.getManager().transaction(transactionalEntityManager =>
{
})
}
}
My second implementation of transaction :
async setAll(entity, data)
{
let connection = await this.init()
const queryRunner = connection.createQueryRunner()
await queryRunner.connect()
for (const [key, value] of Object.entries(data))
{
let genre1 = new Genre()
genre1.name = 'toto'
genre1.identifier = 'gt'
genre1.logo = ''
genre1.isActivate = false
await queryRunner.startTransaction()
await queryRunner.manager.save(genre1)
await queryRunner.commitTransaction()
await queryRunner.release()
}
}
NB : The second implementation persist correctly the first object but not the others.
How can manage many typeorm Transaction created into loop for persist lot of data ?
async setAll(entity, data) {
let connection = await this.init()
const queryRunner = connection.createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction()
try {
for await (const [key, value] of Object.entries(data)) {
let genre1 = new Genre()
genre1.name = 'toto'
genre1.identifier = 'gt'
genre1.logo = ''
genre1.isActivate = false
const newGenre= queryRunner.manager.create(Genre,genre1)
await queryRunner.manager.save(newGenre)
}
await queryRunner.commitTransaction()
} catch {
await queryRunner.rollbackTransaction()
} finally {
await queryRunner.release()
}
List responseJson;
List eventDetails = [];
Future<String> fetchPost() async {
final response = await http.get(
"https..",
headers: {
HttpHeaders.AUTHORIZATION:
"Bearer ..."
});
for(var i = 0; i< (responseJson?.length ?? 0);i++) {
final eventDetailsResponse = await http.get(
"https:.." + responseJson[i]["id"].toString(),
headers: {
HttpHeaders.AUTHORIZATION:
"Bearer .."
});
eventDetails.add(json.decode(eventDetailsResponse.body));
}
this.setState(() {
responseJson = json.decode(response.body);
print(eventDetails);
print(responseJson);
});
Hi there! I have an array defined, I filled the array in with elements, each element is the result of an API call, and then I use the function this.setState to trigger the list view to update, on the first run eventDetails is empty, however on the second run it gets filled in with data, how can I make it await?
Thank you in advance.
Are you trying to populate your view from a future?
If that's the case, have a look at FutureBuilder.
I'm trying to code sql access to a database using sqljocky in Dart. As I want to make some computation with the result returned by my database Handler, the method return a Future.
But when I try to run it, I'm getting the following error:
Uncaught Error: The null object does not have a method 'then'`
I've ran the debugger and found that this error raise on:
db.query('select * from user where email="$email"').then(...)
but the catchError clause doesn't fire.
My handler method is:
// db is a ConnectionPool
Future<Map<String,String>> queryUser(String email){
print(email);
db.query('select * from user where email="${email}"').then((result) { // here raise the error
Map<String,String> results = new Map<String,String>();
result.forEach((row){
results['status'] = '200';
results['ID'] = row[0];
results['Image'] = row[1];
results['Name'] = row[2];
results['Email'] = row[3];
results['Password'] = row[4];
});
return results;
}).catchError((error){
Map<String,String> results = new Map<String,String>();
results['status'] = '500';
return results;
});
}
And the method that call this handler is:
List getUser(String email) {
Future<Map<String,String>> result = dbhandler.queryUser(email);
result.then((Map<String,String> result) {
String statuscode = result['status'];
result.remove('status');
String json = JSON.encode(result);
List pair = new List();
pair.add(statuscode);
pair.add(json);
return pair;
});
If I run the query directly in phpmyadmin, it return correct data, so it is correct.
Can someone give me a hint about how to solve it?
The queryUser() method will always return null, as there is no return statement. In the next release of Dart there will be a static hint warning for this, but at the moment there is none.
Perhaps the code below is what you meant to do. Note the initial return statement before db.query(), and the extra result.toList() call. I haven't tested this, so there's probably a typo or two.
Future<Map<String,String>> queryUser(String email){
print(email);
return db.query('select * from user where email="${email}"')
.then((result) => result.toList())
.then((rows) {
var row = rows.single;
Map<String,String> results = new Map<String,String>();
results['status'] = '200';
results['ID'] = row[0];
results['Image'] = row[1];
results['Name'] = row[2];
results['Email'] = row[3];
results['Password'] = row[4];
return results;
}).catchError((error){
Map<String,String> results = new Map<String,String>();
results['status'] = '500';
return results;
});
}
You can also make this a bit cuter using map literals:
Future<Map<String,String>> queryUser(String email){
return db.query('select * from user where email="${email}"')
.then((result) => result.toList())
.then((rows) => <String, String> {
'status': '200',
'ID': rows.single[0],
'Image': rows.single[1],
'Name': rows.single[2],
'Email': rows.single[3],
'Password': rows.single[4] })
.catchError((error) => <String, String> {'status': '500'});
}
Finally I found the answer using Completer to control the Future object, but the real problem was, as Greg Lowe said, that my methods doesn't return anything as they come to end before the then clause.
Using completer, I've made my query method as:
Future<Map<String,String>> queryUser(String email){
Completer c = new Completer();
db.query('select * from user where email="$email"').then((result) {
Map<String,String> results = new Map<String,String>();
result.forEach((row){
results['status'] = '200';
results['ID'] = row[0].toString();
results['Image'] = row[1];
results['Name'] = row[2];
results['Email'] = row[3];
results['Password'] = row[4];
}).then((onValue){
c.complete(results);
});
}).catchError((error){
Map<String,String> results = new Map<String,String>();
results['status'] = '500';
c.completeError((e) => print("error en queryUser"));
});
return c.future;
}
I also solved an error when using the foreach method, at first I supposed it return nothing, but after that, I noticed that it return a Future, so I added a then clause.
And my getUser method:
Future<List> getUser(String email) {
Completer c = new Completer();
Future<Map<String,String>> result = dbhandler.queryUser(email);
result.then((Map<String,String> result) {
String statuscode = result['status'];
result.remove('status');
String json = JSON.encode(result);
List pair = new List();
pair.add(statuscode);
pair.add(json);
c.complete(pair);
});
return c.future;
}
After those changes, everything works right