I'm writing an app using Flutter and i have to make a transaction using the Firestore.instance.runTransaction(Transaction tx)
method. In my Transaction object (or method) i have to update some data using the document reference.
_firestore.runTransaction((Transaction x) async {
await x.update(Aref, {'data': itemA - y});
await x.update(Bref, {'data': itemB + y});
})
When the code is running it throw an exception (Here the console log):
E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): Failed to handle method call result E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): java.lang.IllegalStateException: Task is already complete E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source:8) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.tasks.zzu.zzdr(Unknown Source:8) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.tasks.zzu.setResult(Unknown Source:3) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at com.google.android.gms.tasks.TaskCompletionSource.setResult(Unknown Source:2) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$3$1.success(CloudFirestorePlugin.java:283) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at io.flutter.plugin.common.MethodC hannel$IncomingResultHandler.reply(MethodChannel.java:169) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at io.flutter.view.FlutterNativeView.handlePlatformMessageResponse(FlutterNativeView.java:187) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at android.os.MessageQueue.nativePollOnce(Native Method) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at android.os.MessageQueue.next(MessageQueue.java:325) E/MethodChannel#plugins.flutter.io/cloud_firestore(32612): at android.os.Looper.loop(Looper.java:142)
The error happens when there are multiple call to the firestore api concurrently, you have to nest each funcion inside the "whenComplete(() {}" method of the previous function. This is the wrong code :
g.f.runTransaction((transaction) async {
DocumentSnapshot snap =
await transaction.get(/my code);
await transaction.update(/my code).whenComplete(() {});
});
g.f.runTransaction((transaction) async {
DocumentSnapshot freshSnap =
await transaction.get(/my code));
await transaction.update(/my code).whenComplete(() {});
}); //here is the problem!! I'have to nest this inside "whenComplete(() {})
this is the error:
E/MethodChannel#plugins.flutter.io/cloud_firestore( 5337): Failed to handle method call result E/MethodChannel#plugins.flutter.io/cloud_firestore( 5337): java.lang.IllegalStateException: Task is already complete
this is right code
g.f.runTransaction((transaction) async {
DocumentSnapshot snap =
await transaction.get(/my code);
await transaction.update(/my code).whenComplete(() {
g.f.runTransaction((transaction) async {
DocumentSnapshot freshSnap =
await transaction.get(/my code);
await transaction.update(/my code);
}).whenComplete(() {});
});
});
I had the same problem. Try this. Hope it Helps FIrestore Transaction Handler
This is a subroutine to help handle the failing transaction issue when using runTransaction
function in cloud_firestore
pub for Flutter
This code is a solution to errors with cloud_firestore package for Flutter for the following errors 1. PlatformException(Error performing Transaction#get, Transaction has already completed., null)
2. DoTransaction failed: Document version changed between two reads.
From my experience, I realized that the issue occurs when I try to update a record which was only just created. For some reason the tx.get()
(see the example for runTransaction
in https://pub.dev/packages/cloud_firestore ) is unable to get the record that was just created and the update operation fails. I found that if we wait for a bit and try again, we will be able to get the record and update it. To make things easy, I have created a function that does the update and run transaction for you.
Here is an example :
await fcmTransactionHandler(
postRef: postRef, // This is your DocumentReference that you want to update
key: 'myfield', // This is the field that you want to update
validationFunction: updateMyField, // This is a function that allows you to check
// some condition in the record you 'get' before updating
);
validationFunction
takes a dynamic value (value of the field you want to update) as input and gives the {}
value that you will update in the record. For example, if I want to update a field myfield
if it is say true
, then I will create a function updateMyField
as follows
Map<String, dynamic> updateMyField({dynamic value}) {
return value ? <String, dynamic>{'myfield': true} : <String, dynamic>{'myfield': value};
}
Then this function is passed to validationFunction
. Why is it designed like this ? As you have seen, we are trying to get the record after a while and then update it. What if the record has been updated by someone else in between ? In that case, we have to validate the data we get when the record is available for us, to prevent any incorrect updates. validationFunction
helps us do that.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.