Search code examples
dartstreamgeneratordart-async

Yield values from generator from async functions without awaiting


I need to run many async functions concurrently and yield the results as they complete, order doesn't matter.

Here is what i have in a simplified example, of course this does not work right because it's waiting for every response before moving to the next request.

 Stream<String> stringGenerator(List<http.Request> requests) async* {
    final httpClient = http.Client();
    for (var req in requests) {
      final response = await httpClient.send(req);
      yield response.headers['example'];
    }
  }

Solution

  • Could you try and see if this is working for you?

    Stream<String> stringGenerator(List<http.Request> requests) {
      final controller = StreamController<String>();
      final httpClient = http.Client();
    
      Future.wait(requests.map((req) => httpClient
              .send(req)
              .then((response) => controller.add(response.headers['example']!))))
          .whenComplete(() => controller.close());
    
      return controller.stream;
    }
    

    More correct would be this, since we don't want to generate events before we are listening for them according to the documentation for StreamController. It is really not an issue for internal usage since StreamController does buffer events until a listener are subscribed:

    Stream<String> stringGenerator(List<http.Request> requests) {
      final controller = StreamController<String>();
      
      controller.onListen = () {
        final httpClient = http.Client();
    
        Future.wait(requests.map((req) => httpClient
            .send(req)
            .then((response) => controller.add(response.headers['example']!))))
            .whenComplete(() => controller.close());
      };
    
      return controller.stream;
    }