Allen Hu
Allen Hu

Reputation: 655

Is there a way to get notified when a dart stream gets its first result?

I currently have an async function that does the following:

  1. Initializes the stream
  2. Call stream.listen() and provide a function to listen to the stream.
  3. await for the stream to get its first result.

The following is some pseudo code of my function:

Future<void> initStream() async {
  // initialize stream
  var stream = getStream();
  // listen
  stream.listen((result) {
    // do some stuff here
  });
  // await until first result
  await stream.first; // gives warning
}

Unfortunately it seems that calling stream.first counts as listening to the stream, and streams are not allowed to be listened by multiple...listeners?

I tried a different approach by using await Future.doWhile() Something like the following:

bool gotFirstResult = false;
Future<void> initStream() async {
  var stream = getStream();
  stream.listen((result) {
    // do some stuff here
    gotFirstResult = true;
  });
  await Future.doWhile(() => !gotFirstResult);
}

This didn't work for me, and I still don't know why. Future.doWhile() was successfully called, but then the function provided to stream.listen() was never called in this case.

Is there a way to wait for the first result of a stream? (I'm sorry if I didn't describe my question well enough. I'll definitely add other details if needed.) Thanks in advance!

Upvotes: 9

Views: 4628

Answers (2)

Pavel
Pavel

Reputation: 5876

Another way, without creating new stream, is to use Completer. It allows you to return a Future which you can complete (send value) later. Caller will be able to await this Future as usual.

Simple example:

Future<int> getValueAsync() {
  var completer = Completer<int>();
  Future.delayed(Duration(seconds: 1))
    .then((_) {
      completer.complete(42);
    });
  return completer.future;
}

is equivalent of

Future<int> getValueAsync() async {
  await Future.delayed(Duration(seconds: 1));
  return 42;
}

In your case:

Future<void> initStream() {
  var stream = getStream();
  var firstValueReceived = Completer<void>();
  stream.listen((val) {
    if (!firstValueReceived.isCompleted) {
      firstValueReceived.complete();
    }
    // do some stuff here
  });
  return firstValueReceived.future;
}

Upvotes: 9

Pavel
Pavel

Reputation: 5876

One way is converting your stream to broadcast one:

var stream = getStream().asBroadcastStream();
stream.listen((result) {
  // do some stuff here
});
await stream.first;

Upvotes: 12

Related Questions