背景
有时候,我们会遇到这种情况,事件流从一个switch到另一个,某种情况下需要从头再来一遍,比如A->B->C->A->……循环不断,这时候怎么写比较好呢?
一种写法是在subscribe里面调用“调用自身的函数”
let obA = ……
function logic(){
obA.subscribe(...,()=>{
logic()
})
}
上面的伪代码里面调用了logic函数后,我们开始执行obA这个Observable的逻辑,完成后我们会立即再次调用logic,形成循环。
这种写法的不够优雅,会反复的订阅事件流,当然也不能说是错的,但是在某些场合就不适用了,比如A->B->C->D->B->C->……可能循环在B-C这段,那么上面的写法就实现不了。下面我提供一种业务场景来说明如何写
业务逻辑
- 执行一个远程请求获取敏感数据
- 如果返回特定的错误信息则进行弹窗验证身份(要求输入短信验证码)
- 验证不通过则再次验证,直到通过为止
- 验证通过,则返回有效数据
涉及操作符
- catchError
- switchMapTo
变量定义
let getDataOb //获取数据的Observable
let verifyOb //身份验证请求Observable
当订阅getDataOb的时候会发出请求,如果不是正常返回值,就抛出错误,此处我们省略了该Observable的创建过程
当订阅verifyOb 的时候会发出身份验证的请求,这里也省略了该Observable的创建过程,这个Observable可能是一个有多个步骤的事件流,比如需要等待用户点击发送验证码按钮等。跟我学Rx编程———获取验证码
实现过程(伪代码)
let verifyOp = catchError(err=>{
if(err.code==VERIFY){
return verifyOb.pipe(verfiyOp)
}
})
getDataOb.pipe(verifyOp).subscribe(……)
伪代码已经简化了,去掉了不相干的成分。verifyOp 是一个操作符,放到外面声明,在其内部可以访问到自身的引用。
我们用到了操作符catchError
,用来捕获错误,当产生特定的错误的时候,我们会返回一个Observable——verifyOb.pipe(verfiyOp)
,这个Obserable如果还发生错误,就会继续被捕获,形成循环,或者说递归,因为是异步请求所以严格的说不能称为递归。
思考
我们写同步代码的时候一般就用
while(condition){
}
来进行循环即可
但是当业务很复杂,需要很多异步逻辑的时候,这种写法的复杂度就会成几何级上升。此时我们使用Rx编程,可以让代码在形式上仍然保持类似递归调用的样子,方便理解整体业务,代码也更为健壮。
今天的案例可以用于其他类似的循环逻辑结构中,不局限于例子中的业务逻辑。