Some explanations about completer

714 views Asked by At

Hello everyone

I have one class 'Example' who needs to do some computation. I call start() which call _next(). During the computation _next() calls itself couple of time but in my example I simulate that with a Timer. here is my code

import "dart:async";


main() {

    Example ex = new Example();

    for (var i = 0 ; i < 3 ; i++) {
        ex.start().then((nbr) {
            print(nbr);
        });
    }

}

class Example {

    /// for _next
    Completer _insideCompleter;

    /// from start() to outside
    Completer _outsideCompleter;

    Example();

    /// start is just a better public api than next when we start the exercise
    Future<int> start() {

        _insideCompleter = new Completer();
        _outsideCompleter = new Completer();


        _next().then((int value) {
            print("value: $value");
            _outsideCompleter.complete(value);

        }).catchError((message) {
            print("information: $message");
        });

        return _outsideCompleter.future;
    }


    /// _next handle the flow with the status
    Future<int> _next() {

        new Timer(new Duration(seconds: 6), () {
            _insideCompleter.complete(15);
        });

        return _insideCompleter.future;
    }
}

it finishes with : Bad state: Future already completed. but as you can see in the start(). the Completer are re-created with new. So I don't understand why it is already complete.

If anybody can explain why it's not correct to code like that and maybe give me some interesting links it would be great

Cheers!

2

There are 2 answers

3
Günter Zöchbauer On

I'm not entirely sure what your intention with the code is but I think you should either

Example ex = new Example();

for (var i = 0 ; i < 3 ; i++) { // create a new Example() for each iteration here
    ex.start().then((nbr) {     // or ensure that the next iteration is not executed 
        print(nbr);             // before the previous is completed.
    });
}

with this code ex.start() is called 3 times before the first call is completed.

0
Vink On

Here the main issue is about completers called in the callback function

_next().then((int value) {
            print("value: $value");
            _outsideCompleter.complete(value); // this line

        })

and

 new Timer(new Duration(seconds: 6), () {
            _insideCompleter.complete(15); // this line
        });

Because this 2 function are called after your loop and your completer are attribute, all the callback will use the latest _outsideCompleter and _insideCompleter created.

So after than one of the callback have "consume" your completer, the others will create exception of 'Bad state: Future already completed'

here a version that works

import "dart:async";


main() {

  Example ex = new Example();

  for (var i = 0 ; i < 3 ; i++) {
    ex.start().then((nbr) {
      print(nbr);
    });
  }

}

class Example {

  Example();

  /// start is just a better public api than next when we start the exercise
  Future<int> start() {

    var outsideCompleter = new Completer(); // create localy each times


    _next().then((int value) {
      print("value: $value");
      outsideCompleter.complete(value);

    }).catchError((message) {
      print("information: $message");
    });

    return outsideCompleter.future;
  }


  /// _next handle the flow with the status
  Future<int> _next() {
    var insideCompleter = new Completer(); // create localy each times

    new Timer(new Duration(seconds: 6), () {
      insideCompleter.complete(15);
    });

    return insideCompleter.future;
  }
}