0

我需要同时运行许多异步函数并在它们完成时产生结果,顺序无关紧要。

这是我在一个简化示例中的内容,当然这不起作用,因为它在移动到下一个请求之前等待每个响应。

 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'];
    }
  }
4

2 回答 2

2

你能试试看这对你有用吗?

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;
}

更正确的是这样,因为我们不想在根据文档监听它们之前生成事件StreamController。这实际上不是内部使用的问题,因为StreamController在订阅侦听器之前会缓冲事件:

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;
}
于 2021-03-07T12:04:39.737 回答
0

@julemand101 解决方案的通用替代方案,适用于任何类型的期货:

Stream<T> fromFutures<T>(Iterable<Future<T>> futures) {
  var pending = 0;
  var controller = Controller<T>();
  for (var future in futures) {
    pending++;
    future.then((v) {
      controller.add(v);
      if (--pending == 0) controller.close();
    }, onError: (e, s) {
      controller.addError(e, s);
      if (--pending == 0) controller.close();
    });
  }
  return controller.stream;
}

您可以将其指定stringGenerator为:

Stream<String> stringGenerator(List<http.Request> requests) async* {
  var client = http.Client();
  yield* fromFutures(requests.map(client.send));
}
于 2021-03-08T09:27:35.213 回答