简体   繁体   中英

Flutter/cloud-firestore “Task is already complete” Exception

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

Firestore-Transaction-Handler

This is a subroutine to help handle the failing transaction issue when using runTransaction function in cloud_firestore pub for Flutter

The Problem

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.

The Solution

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM