RXJS:组合可观察对象以按顺序发出

时间:2019-11-12 14:08:52

标签: rxjs observable

我有三个可观察的ob1,ob2,ob2,它们都是API调用,我需要按顺序执行它们,并将值作为数组发出,如果有任何错误,我希望捕获错误catchError并停止进一步的api调用,例如:

someCombine(ob1, ob2.pipe(tap(val=> doSomething)), ob3.pipe(tap(val=> 
doSomething))).pipe(catchError((err)=> of(err))).subscribe(combinedVal=>{
if(err) {
processErr
} 
else 
doSomething
});

我尝试使用zip

 zip(ob1, ob2.pipe(tap(val=> doSomething)), ob3.pipe(tap(val=> 
    doSomething))).pipe(catchError((err)=> of(err))).subscribe(combinedVal=>{
    if(err) {
    processErr
    } 
    else 
    doSomething
    });

但是在第一个zip结束之前,可观察到zip,我已经尝试了concat

  concat(ob1, ob2.pipe(tap(val=> doSomething)), ob3.pipe(tap(val=> 
    doSomething))).pipe(catchError((err)=> of(err))).subscribe(combinedVal=>{
    if(err) {
    processErr
    } 
    else 
    doSomething
    });

,但是对于每个可观察到的完成,将分别发出值。

如何处理这种情况。

2 个答案:

答案 0 :(得分:0)

我想是这样的

import { tap, delay, catchError } from 'rxjs/operators';
import { of, throwError, combineLatest } from 'rxjs';


// this is just to simulate the requests
const ob1$ = of('ob1').pipe(delay(5000)),
      ob2$ = of('ob2').pipe(delay(1000)),
      ob3$ = of('ob3').pipe(delay(3000));


// abstract to reuse, since we'll catch for each observable
const handleError = (err) => {
  // process error
  console.log('process error', err);
  return throwError(err);
};

const doSomething = (val) => {
  // do something
  console.log('do something', val);
};


combineLatest(
  ob1$.pipe(catchError(handleError), tap(doSomething)),
  ob2$.pipe(catchError(handleError), tap(doSomething)),
  ob3$.pipe(catchError(handleError), tap(doSomething))
).subscribe(value => console.log(value));

// value = ["ob1", "ob2", "ob3"].
// Only emits when/if all 3 observables emit/complete.

我意识到这并不是您真正想要的,因为请求是同时发出的,并且以它们可以发出的任何顺序发出,但是结果数组将被正确排序,并且只会在最后发出。每个可观察对象将触发自己的tapcatchError。除非您的用例非常特殊,否则应该更好。

答案 1 :(得分:0)

bufferCount
bufferCount将发出最后x次发出,因此,如果您知道有3个api请求,则在确认后可以使用bufferCount(3)

const { of, concat, operators: { delay, bufferCount } } = rxjs;

const ob1$ = of ('ob1').pipe(delay(5000)),
  ob2$ = of ('ob2').pipe(delay(1000)),
  ob3$ = of ('ob3').pipe(delay(3000));

concat(ob1$, ob2$, ob3$).pipe(bufferCount(3)).subscribe({
  next: next => console.dir(next) // ['ob1', 'ob2', 'ob3]
});
<script src="https://unpkg.com/@reactivex/rxjs@6.5.3/dist/global/rxjs.umd.js"></script>

-或-

自定义管道
drain管道运算符将在源可观察到的操作完成后立即发出并立即发出所有发出的消息,因此在联系之后,您可以得到所有结果,并且它将处理任意数量的发出消息。

const { of, concat, Subscriber, operators: { delay } } = rxjs;

class DrainSubscriber extends Subscriber {
	constructor(destination) {
    super(destination);
    this.result = [];
  }
  _next(value) {
    this.result.push(value);
  }
  _complete() {
  	this.destination.next(this.result);
    super._complete();
  }
}

class DrainOperator {
	call(subscriber, source) {
  	return source.subscribe(new DrainSubscriber(subscriber));
  }
}

function drain() {
	return (source) => source.lift(new DrainOperator());
}

const ob1$ = of ('ob1').pipe(delay(5000)),
  ob2$ = of ('ob2').pipe(delay(1000)),
  ob3$ = of ('ob3').pipe(delay(3000));

concat(ob1$, ob2$, ob3$).pipe(drain()).subscribe({
  next: next => console.dir(next) // ['ob1', 'ob2', 'ob3]
});
<script src="https://unpkg.com/@reactivex/rxjs@6.5.3/dist/global/rxjs.umd.js"></script>

在TypeScript https://stackblitz.com/edit/typescript-1yrvm2-drain-operator?file=index.ts

-promise alt-

基于承诺的替代

const { of, concat, operators: { delay } } = rxjs;

const ob1$ = of('ob1').pipe(delay(5000)),
  ob2$ = of('ob2').pipe(delay(1000)),
  ob3$ = of('ob3').pipe(delay(3000));

async function resolve() {
  const res1 = await ob1$.toPromise();
  const res2 = await ob2$.toPromise();
  const res3 = await ob3$.toPromise();
  return [res1, res2, res3];
}

resolve().then(results => {
  console.dir(results); // ['ob1', 'ob2', 'ob3]
});
<script src="https://unpkg.com/@reactivex/rxjs@6.5.3/dist/global/rxjs.umd.js"></script>

相关问题