如何在Observable方法内按顺序调用异步函数
我有如下方法:
public classMethod( payload: Payload, ): Observable<Result> { const { targetProp } = payload; let target; return this.secondClass.secondClassMethod({ targetProp }).pipe( delayWhen(() => // some other actions ), ); }
硬性执行要求:调用this.secondClass.secondClassMethod之前必须完成target变量的赋值,该顺序要求不可调整。
我原本想直接在方法内部用await调用常规异步方法给target赋值,预期写法如下:
public classMethod( req: classMethodRequest, ): Observable<classMethodResponse> { const { targetProp } = req; let target; /** ** 在此处调用异步方法完成target赋值 ** 类似: ** target = await someAsyncMethod(targetProp) **/ return this.secondClass.secondClassMethod({ targetProp }).pipe( delayWhen(() => ), ); }
核心诉求:调用classMethod时,方法内部先完成target变量的赋值,后续返回的Observable结构中可以直接使用该target变量。
我之前尝试过将异步方法包裹在from()中实现:
from( (async () => { target = await this.someAsyncMethod.setTarget(targetProp); })(), );
但实测发现这种写法会和后续的返回逻辑并行执行:
return this.secondClass.secondClassMethod({ targetProp }).pipe( delayWhen(() => ), );
并行执行完全不符合需求。
不要单独声明外层target变量再零散写赋值逻辑,直接用RxJS的顺序控制操作符把异步初始化逻辑串入整条数据流即可,两种可直接落地的写法:
写法1:from+switchMap串联(推荐)
将Promise异步逻辑转为Observable后,用switchMap切换到后续secondClassMethod调用流,此时异步赋值已经完成,后续流中可直接获取target值:
public classMethod( req: classMethodRequest, ): Observable<classMethodResponse> { const { targetProp } = req; return from(this.someAsyncMethod.setTarget(targetProp)).pipe( // 此处拿到异步方法返回的target值 switchMap(target => this.secondClass.secondClassMethod({ targetProp }).pipe( delayWhen(() => { // 此处可直接使用target变量,编写其他业务逻辑 }) ) ) ); }
该写法严格按顺序执行:先等setTarget异步执行完成拿到target,才会触发secondClassMethod调用,完全满足顺序要求。
写法2:保留外层变量的适配方案
如果因特殊业务原因必须将target声明在流外部,不要写独立的from()片段,要把初始化逻辑作为流的起点,保证后续逻辑在其执行完成后再触发:
public classMethod( req: classMethodRequest, ): Observable<classMethodResponse> { const { targetProp } = req; let target; return defer(() => this.someAsyncMethod.setTarget(targetProp)).pipe( tap(res => target = res), // 异步完成后给外层target赋值 // 赋值完成后才会执行后续secondClassMethod调用 switchMap(() => this.secondClass.secondClassMethod({ targetProp })), delayWhen(() => { // 此处可直接使用已赋值完成的target }) ); }
这里用defer而非直接写from的好处是,只有当观察者订阅该Observable时,才会真正触发setTarget异步调用,符合RxJS的懒执行约定。
之前写法的核心问题:单独书写的
from(...)既没有被订阅,也没有串入最终返回的Observable管道,和后续返回的流是完全独立的两个执行单元,自然会出现并行执行的问题。只要把所有需要按顺序执行的逻辑都串入同一个Observable管道,用对应高阶操作符控制执行顺序,就不会出现并行问题。
内容的提问来源于stack exchange,提问作者watchmecode

