如何合并mergeMap observables调用并只为整个observable返回一个值

时间:2019-04-30 00:48:58

标签: javascript angular typescript rxjs

我在使用rxjs 6.5的Typescript / Angular中有这种情况:

   main(){

        const properties = ['session', 'user'];
        const source: Observable<any> = from(properties);

        source
            .pipe(
                mergeMap(key => this.getKey().map((value) => ({key: key, value: value}))),
                tap((result) => {
                    // sending the result elsewhere;
                }),
            ).subscribe(
            (result) => {
                console.log('Final result ->', result);
            }
        );

        console.log('\n');

    }

    getKey(): Observable<any> {
        // Mock function that returns an observable that emits 1 value
        const observable = from(['test']);

        return observable;
    }

输出为:

Final result -> {key: "session", value: "test"}
Final result -> {key: "user", value: "test"}

第一个问题: 我如何以最优雅的方式以原始订阅的价格返回1个值,并结合内部可观测值的结果?

我想要的输出以及这种订阅方式(因为我希望此合并操作在管道中)将是:

(...).subscribe(
(result) => {console.log('Final Result:', result}
)

OUTPUT:

Final result -> [{key: "session", value: "test"}, {key: "user", value: "test"}]

第二个问题 如果我不关心内部可观测值的结果,如何只返回1值或如何知道所有内部可观测值何时完成?

谢谢。

5 个答案:

答案 0 :(得分:0)

第一个问题,您可以使用scan来处理和累积输出

 mergeMap(key => from(this.getKey())),
 scan((acc,curr) =>acc.concat([{key: curr.key, value: curr.value}]),[])),

2n个问题

使用first()运算符仅从内部可观察到的一个输出 将finalize()附加到内部可观察对象,当内部可观察对象完成时会触发。或使用last()来获取最后的累积结果

 mergeMap(key => from(this.getKey())),
 scan((acc,curr) =>acc.concat([{key: curr.key, value: curr.value}]),[])),
 first()

答案 1 :(得分:0)

使用减少

.pipe(
  reduce((results, result) => {
    results.push(result);
    return results;
  }, [])
)

只有其他所有对象都发出后,结果的Observable才会发出,并且发出的值将是所有结果的数组。

答案 2 :(得分:0)

这是一个带注释的示例,可以帮助您澄清有关您所询问的订阅过程的问题。

第一季度:

正如另一个答案所指出的,reduce运算符是您想要包含在source管道中的内容。 reduce的一个关键细节是它仅在完成可观察到的相应源时发出。相反,如果您希望在内部可观测量完成时发射,则scan是合适的。与后者的另一个区别是,它不需要源代码完成。

第二季度:

关于这个问题,请参考下面的示例,并将处理管道的每个参数都视为单个请求的生存期。在此,完成是隐式的。它在处理内部可观测值的最后一个值之后发生。

但是,如果内部可观对象没有界限,那么就不可能知道所有内部可观对象何时完成。在这种情况下,您会发现reduce()不起作用。

const { from, of, Subject } = rxjs;
const { mergeMap, map, tap, reduce, scan } = rxjs.operators;

// Use a subject to simulate processing.
// Think of each argument as a request to the processing pipeline below.
const properties = new Subject();

// Establish processing pipeline
const source = properties.pipe(
  // `mergeMap` here flattens the output value to the combined inner output values
  mergeMap(props =>
    // Each item inside the argument should be piped as separate values
    from(props).pipe(
      // `mergeMap` here flattens the output value to `{ key, value }`
      mergeMap(key =>
        of('test').pipe(
          map(value => ({ key, value })),
        ),
      ),
      // Unlike `scan`, `reduce` only emits upon "completion".
      // Here, "completion" is implicit - it is after the last
      // element of `from(props)` has been processed.
      reduce((a, i) => [...a, i], []),
    )
  ),  
);

// Subscribe to the pipeline to observe processing.
source.subscribe(console.log);

// Trigger a processing request with an argument
properties.next(['session', 'user']);

// Trigger another processing request
properties.next(['session', 'user']);
<script src="https://unpkg.com/rxjs@6.5.1/bundles/rxjs.umd.min.js"></script>

答案 3 :(得分:0)

Q1::您需要一个 toArray —它将所有流值组合到一个数组中:

toArray example

第二季度::要忽略流中的所有值,并在完成时发出一个值

concat(
  source$.pipe(ignoreElements()),
  of(true)
)

emit a value upon source completion

请参见"Emit a value upon source completion"在操场上的示例

答案 4 :(得分:0)

要获得mergeMap的所有响应的合并结果,您还可以尝试如下操作:

return this.request1().pipe(
  mergeMap(res1=> this.request2(res1.id).pipe(
    map(res2=> {
      return {
        res1: res1,
        res2: res2
      }
    })
  ))
)