RxJS Observable.if双分支均执行问题:getUserFromApi提前执行的排查与解决
getUserFromApi() always execute even when Observable.if() should skip it? Great catch—you're exactly right that getUserFromApi() is being called prematurely, even when your condition says it shouldn't run. Let's break down the root cause and fix this step by step.
The Root Cause: Immediate Argument Evaluation
In JavaScript (and TypeScript), when you pass an expression as a function argument, that expression gets evaluated right away—before the function you're calling even starts running.
In your original code, when you write:
Observable.if(() => this.user == null, this.getUserFromApi().map(...), Observable.of(this.user) );
The JavaScript engine first runs this.getUserFromApi() to compute the second argument for Observable.if(). It doesn't wait for the condition check—it just evaluates all arguments upfront. That's why your API call runs every single time, no matter what this.user is.
The Fix: Use Observable Factory Functions
Luckily, Observable.if() (from RxJS 5, which you're using) supports passing factory functions for its true/false cases. A factory function is just a function that returns an Observable. When you use a factory, Observable.if() will only execute that function if the corresponding condition is met—so your API call won't run unless this.user is actually null.
Here's the corrected getCurrentUser() method:
getCurrentUser(): Observable<User> { return Observable.if( () => this.user == null, // Factory function: runs ONLY if condition is true () => this.getUserFromApi().map((user: User) => { this.user = user; return this.user; }), // Optional: factory function for the else case (keeps consistency) () => Observable.of(this.user) ); }
Quick Note on Your Test
One small thing to point out about your test: when you call deleteCurrentUser(), you set this.user = new User('', '', '')—this means this.user is not null (it's an empty User instance), so your condition this.user == null will be false. That's why your test expects an empty username and works, but with the original code, getUserFromApi() still ran unnecessarily. The fix above stops that unnecessary execution.
Bonus: Modern RxJS (RxJS 6+) Alternative
If you're able to upgrade to RxJS 6 or later, the iif() operator replaces Observable.if(), and the pattern is similar (you can still use factory functions to defer execution):
import { iif, of } from 'rxjs'; import { map } from 'rxjs/operators'; // ... getCurrentUser(): Observable<User> { return iif( () => this.user == null, // Wrap the API call in a factory to defer execution () => this.getUserFromApi().pipe( map(user => { this.user = user; return this.user; }) ), of(this.user) ); }
内容的提问来源于stack exchange,提问作者Arikael

