简体   繁体   中英

Dart/Flutter - "yield" inside a callback function

I need to yield a list for a function; however, I want to yield the list from within a callback function, which itself is inside the main function - this results in the yield statement not executing for the main function, but rather for the callback function.

My problem is very similar to the problem that was solved here: Dart Component: How to return result of asynchronous callback? but I cannot use a Completer because I need to yield and not return.

The code below should describe the problem better:

Stream<List<EventModel>> fetchEvents() async* { //function [1]
    Firestore.instance
        .collection('events')
        .getDocuments()
        .asStream()
        .listen((snapshot) async* { //function [2]
      List<EventModel> list = List();
      snapshot.documents.forEach((document) {
        list.add(EventModel.fromJson(document.data));
      });

      yield list; //This is where my problem lies - I need to yield for function [1] not [2]
    });
  }

Instead of .listen which handles events inside another function you can use await for to handle events inside the outer function.

Separately - you might want to reconsider the pattern when you yield List instances that are still getting populated inside an inner stream callback...

Stream<List<EventModel>> fetchEvents() async* {
  final snapshots =
      Firestore.instance.collection('events').getDocuments().asStream();
  await for (final snapshot in snapshots) {
    // The `await .toList()` ensures the full list is ready
    // before yielding on the Stream
    final events = await snapshot.documents
        .map((document) => EventModel.fromJson(document.data))
        .toList();
    yield events;
  }
}

I would like to add a suggestion for improvement here. The suggested await for solution should be avoided in some cases as it is non dismissible listener and it newer stops listening so this might lead to memory leaks. You could as well use .map to transform the stream yield results like so (havent tried to compile it, but main idea should be clear):

Stream<List<EventModel>> fetchEvents() { // remove the async*
    Firestore.instance
        .collection('events')
        .getDocuments()
        .asStream()
        .map((snapshot) { // use map instead of listen
      List<EventModel> list = List();
      snapshot.documents.forEach((document) {
        list.add(EventModel.fromJson(document.data));
      });

      return list; // use return instead of yield
    });
  }

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