在写业务逻辑的时候,经常会遇到一些场景,根据某些先决条件进行决策调用后续的接口请求,例如在处理订单支付的时候,如果是新的订单,那么需要调用订单支付接口;如果是已有的订单,那么需要调用重新支付接口,因为这个时候订单已经创建好了,只需要重新支付即可。
要进行编码的时候,自然而然想到的就是使用 iif
。因为 iif
会根据第一个参数的返回结果,返回一个 Observable
,这个 Observable
会订阅其余两个参数中的一个。那么就会写出如下的实现。
processOrder$.pipe(
switchMap((isNewOrder) => iif(
() => isNewOrder === true,
from(fetch('/order')),
from(fetch('/pay')),
)),
).subscribe();
但是上述的代码并不会像想象中的那样执行,观察网络请求,可以看到两个请求都被执行了。
深挖一下 iif
的实现
export function iif<T, F>(condition: () => boolean, trueResult: ObservableInput<T>, falseResult: ObservableInput<F>): Observable<T | F> {
return defer(() => (condition() ? trueResult : falseResult));
}
可以观察到 iif
仅仅是对 defer
的简单封装和调用。那么 defer
又做了什么呢?
export function defer<R extends ObservableInput<any>>(observableFactory: () => R): Observable<ObservedValueOf<R>> {
return new Observable<ObservedValueOf<R>>((subscriber) => {
from(observableFactory()).subscribe(subscriber);
});
}
可以看到 defer
创建了一个 Observable
,将参数执行返回的 Observable
变成了高阶 Observable
,在被订阅的时候变成 Observerable
,类似 switchMap
的效果。
iif
是函数,将函数调用作为参数传递给函数的时候,那么函数首先会被调用,这也是为什么,上述代码均被执行的原因。既然知道了原因,那么解决方法就接踵而来。核心思想是保证相关的路径只有在被需要的时候被执行。最简单的方法就是使用三元运算符,或者 if/else
。
processOrder$.pipe(
switchMap((isNewOrder) =>
isNewOrder === true ?
from(fetch('/order')) :
from(fetch('/pay')),
)),
).subscribe();
如果想要代码更为优雅坚持使用 iif
呢?通过上面关于 defer
实现的分析,可以利用 defer
来实现。
of(true).pipe(
switchMap((result) => iif(
() => result === true,
defer(() => from(fetch('/pay'))),
defer(() => from(fetch('/order'))),
)),
).subscribe();
虽然两个 defer
函数调用作为 iif
参数,在传参的那一刻被调用了,但是对于包裹的内部 Observerble
:from
的调用被推迟到了被订阅的那一刻,所以只有真正被订阅的那个请求才会被执行。