【问题标题】:Convert Stream<Stream<T>> to Stream<T>将 Stream<Stream<T>> 转换为 Stream<T>
【发布时间】:2019-11-08 06:10:48
【问题描述】:

我正在使用rxdart 包来处理 dart 中的流。我一直在处理一个特殊的问题。

请看一下这个虚拟代码:

final userId = BehaviorSubject<String>();

Stream<T> getStream(String uid) {
  // a sample code that returns a stream
  return BehaviorSubject<T>().stream;
}

final Observable<Stream<T>> oops = userId.map((uid) => getStream(uid));

现在我想将oops 变量转换为仅获得Observable&lt;T&gt;。

我发现很难解释清楚。但是让我试试。我有一个流 A。我将流 A 的每个输出映射到另一个流 B。现在我有 Stream&lt;Stream&lt;B&gt;&gt; - 一种 recurrent 流。我只是想听听这个模式产生的最新价值。我怎样才能做到这一点?

【问题讨论】:

标签: dart rxdart


【解决方案1】:

我将列出几种将Stream&lt;Stream&lt;T&gt;&gt; 扁平化为单个Stream&lt;T&gt; 的方法。

1.使用纯飞镖

@Irn 回答,这是一个纯飞镖解决方案:

Stream<T> flattenStreams<T>(Stream<Stream<T>> source) async* {
  await for (var stream in source) yield* stream;
}

Stream<int> getStream(String v) {
    return Stream.fromIterable([1, 2, 3, 4]);
}

void main() {
    List<String> list = ["a", "b", "c"];
    Stream<int> s = flattenStreams(Stream.fromIterable(list).map(getStream));
    s.listen(print);
}

输出:1 2 3 4 1 2 3 4 1 2 3 4

2。使用 Observable.flatMap

Observable 有一个 flatMap 方法,可以展平输出流并将其附加到正在进行的流中:

import 'package:rxdart/rxdart.dart';

Stream<int> getStream(String v) {
    return Stream.fromIterable([1, 2, 3, 4]);
}

void main() {
    List<String> list = ["a", "b", "c"];
    Observable<int> s = Observable.fromIterable(list).flatMap(getStream);
    s.listen(print);
}

输出:1 2 3 4 1 2 3 4 1 2 3 4

3.使用 Observable.switchLatest

将发出 Streams(又名“高阶流”)的 Stream 转换为单个 Observable,该 Observable 发出这些 Streams 中最近发出的项目。

这就是我一直在寻找的解决方案!我只需要内部流发出的最新输出。

import 'package:rxdart/rxdart.dart';

Stream<int> getStream(String v) {
    return Stream.fromIterable([1, 2, 3, 4]);
}

void main() {
    List<String> list = ["a", "b", "c"];
    Observable<int> s = Observable.switchLatest(
            Observable.fromIterable(list).map(getStream));
    s.listen(print);
}

输出:1 1 1 2 3 4

【讨论】:

  • 我正在寻找的解决方案是第三个,谢谢!但是,对于那些登陆这个答案的人,Observable 类已被弃用,请改用Stream&lt;int&gt; s = Rx.switchLatest(Stream.fromIterable(list).map(getStream));。
【解决方案2】:

Stream&lt;Stream&lt;Something&gt;&gt; 很少见,因此没有太多明确的支持。

一个原因是有几种(至少两种)方法可以将事物流组合成事物流。

  1. 您要么依次收听每个流,等待它完成后再开始下一个,然后按顺序发出事件。

  2. 或者您在每个新流可用时监听它,然后尽快从任何流中发出事件。

前者使用async/await很容易写:

Stream<T> flattenStreams<T>(Stream<Stream<T>> source) async* {
  await for (var stream in source) yield* stream;
}

后者更复杂,因为它需要一次监听多个流,并结合它们的事件。 (如果只有StreamController.addStream 一次允许多个流,那么它会容易得多)。您可以为此使用来自package:async 的StreamGroup 类:

import "package:async/async" show StreamGroup;

Stream<T> mergeStreams<T>(Stream<Stream<T>> source) {
  var sg = StreamGroup<T>();
  source.forEach(sg.add).whenComplete(sg.close);
  // This doesn't handle errors in [source].
  // Maybe insert 
  //   .catchError((e, s) {
  //      sg.add(Future<T>.error(e, s).asStream())
  // before `.whenComplete` if you worry about errors in [source].          
  return sg.stream;
}

【讨论】:

    【解决方案3】:

    如果你想要一个 Stream> 返回一个 Stream,你基本上需要将流展平。 flatmap 函数就是您将在此处使用的函数

    public static void main(String args[]) {
        StreamTest st = new StreamTest<String>();
        List<String> l = Arrays.asList("a","b", "c");
        Stream s = l.stream().map(i -> st.getStream(i)).flatMap(i->i);
    }
    
    Stream<T> static getStream(T uid) {
        // a sample code that returns a stream
        return Stream.of(uid);
    }
    

    如果需要第一个对象,则使用 findFirst() 方法

    public static void main(String args[]) {
        StreamTest st = new StreamTest<String>();
        List<String> l = Arrays.asList("a","b", "c");
        String str = l.stream().map(i -> st.getStream(i)).flatMap(i->i).findFirst().get();
    }
    

    【讨论】:

    • flatMap中参数i的类型应该是Stream&lt;T&gt;。这不是我想要的。我想从流中提取 T。
    • 如果您想要流的第一个输出,请添加注释
    • 我需要看最后一个值。但直接在流输出上使用 flatMap 似乎可行:Observable.fromIterable(l).flatMap(i -&gt; st.getStream(i))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 2016-02-29
    • 1970-01-01
    • 2020-03-15
    • 2018-08-18
    • 1970-01-01
    相关资源
    最近更新 更多