[英]Rxjs - Chain http calls with Observables
我想做 3 个 http 调用,它返回一个 boolean 值。 我想要的是仅在上一次调用的结果为true
时才进行下一次调用
IE:
call1()
-> 如果为false
,则停止,如果为true
:
call2()
-> 如果为false
,则停止,如果为true
:
call3()
现在以指示哪个调用返回false
的方式读取结果。
我第一次尝试使用 switchMap:
this.call1()
.pipe(
switchMap((r1) => (r1 ? this.call2() : of(false))),
switchMap((r2) => (r2 ? this.call3() : of(false)))
)
.subscribe((r) => {
console.log('inside subscribe:', r);
});
现在在订阅中我得到的问题是:
'inside subscribe:' false
而且我不知道错误结果是来自 call1,2 还是 3。
所以我的解决方案是将结果保存在外部数组中,如下所示:
// seriel execution with switchMap, we can use each result to the next call
// + save all results
const results = [false, false, false];
this.call1()
.pipe(
// save stage1 result
tap(r1 => results[0] = r1),
// if true, make call2, else return false
switchMap((r1) => (r1 ? this.call2() : of(false))),
// save stage2 result
tap(r2 => results[1] = r2),
// if true, make call3, else return false
switchMap((r2) => (r2 ? this.call3() : of(false))),
// save stage3 result
tap(r3 => results[2] = r3),
)
.subscribe((r) => {
console.log('inside subscribe:', r); // r is the latest value results[2]
console.log('all results:', results) // array of all stages [true,false,false]
});
}
它似乎有效,但也许还有另一种无需外部工作的方法? 仅与 rxjs 运营商合作?
StackBlitz 链接:
https://stackblitz.com/edit/angular-rxjs-playground-zjnh7u?devtoolsheight=33&file=app/app.component.ts
也许这样的事情可以工作
this.call1()
.pipe(
switchMap((r1) => (r1 ? this.call2() :
of(false).pipe(tap(() => console.log('r1 returns false')),
switchMap((r2) => (r2 ? this.call3() :
of(false).pipe(tap(() => console.log('r2 returns false'))),
tap(respOfR3 => if(!respOfR3) {console.log('r3 returns false')})
)
.subscribe((r) => {
console.log('inside subscribe:', r);
});
同时考虑你的第一个 Observable 通知的布尔序列隐含地包含你正在寻找的信息。 实际上,您遇到的第一个 false 的 position 反映了第一个返回false
的调用。
我会将源附加到结果 - 最简单的是元组[number, boolean]
,其中元组/数组中的第一个元素是源。 switchMap
解决方案的一个变体是:
this.call1()
.pipe(
map((result) => [1, result]),
switchMap((r1) => (r1[1] ? this.call2().pipe(map((result) => [2, result])) : of(r1))),
switchMap((r2) => (r2[1] ? this.call3().pipe(map((result) => [3, result])) : of(r2)))
)
.subscribe((r) => {
console.log('inside subscribe:', r);
});
这有一个模式并且有点冗长,我们可以做得更好:
function attachSource(source: number, target: Observable<boolean>): Observable<[number, boolean]> {
return target.pipe(map((result) => [source, result]));
}
attachSource(1, this.call1())
.pipe(
switchMap((r1) => (r1[1] ? attachSource(2, this.call2()) : of(r1))),
switchMap((r2) => (r2[1] ? attachSource(3, this.call3()) : of(r2)))
)
.subscribe((r) => {
console.log('inside subscribe (try2):', r);
});
如果我们再概括一点,给定以下内容:
function attachSource<T>(source: number, target: () => Observable<T>): Observable<[number, T]> {
return target().pipe(map((result) => [source, result]));
}
function series<T>(failed: (arg: T) => boolean, ...factories: (() => Observable<T>)[]): Observable<[number, T]> {
return factories.reduce((agr, cur, index) => {
if (index === 0) {
return attachSource(index+1, cur);
} else {
return agr.pipe(switchMap((prevResult) => failed(prevResult[1]) ? of(prevResult) : attachSource(index+1, cur)));
}
}, empty<[number, T]>());
}
它可以很简单:
series((x: boolean) => x, this.call1.bind(this), this.call2.bind(this), this.call3.bind(this))
.subscribe((r) => {
console.log('inside subscribe (try3):', r);
});
这是一个非常干净的解决方案,使用扩展运算符进行递归调用
const results = [false, false, false];
let count = 0;
this.call().pipe(
tap(response => {
results[count] = response
count++
}
),
expand((response) => response && count < 3
? this.call()
: EMPTY
)
).subscribe((r) => {
console.log('inside subscribe:', r); // r is the latest value results[2]
console.log('all results:', results) // array of all stages [true,false,false]
});
而且你真的不需要多次调用 function (call1, call2, call3),一个就够了
call(): Observable<boolean> {
let bool: boolean = this.randBool();
return of(bool);
}
private randBool(): boolean {
return Math.random() < 0.5
}
https://stackblitz.com/edit/angular-rxjs-playground-zvfpay?file=app/app.component.ts
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.