RxJS使用from处理Promise配合路由跳转时获取旧值原因求解
问题成因与底层逻辑说明
核心根因
这个问题的核心是Promise的热执行特性 + RxJS操作符的参数求值时机,冷热Observable的差异是直接影响因素,事件循环的执行顺序是前置背景但不是核心逻辑。
首先明确两个前置基础规则:
async函数只要被调用就会立即执行,返回的Promise是「热」的:求值逻辑发生在调用时,而非后续等待结果时- RxJS中
switchMapTo的参数会在管道初始化声明时就立即求值,而switchMap传入的回调函数只会在上游事件触发时才会执行
四个选项的执行逻辑拆解
// 补充前置代码上下文 this.storageService.clear().then((_) => { // 清空操作已成功执行,已验证用户数据确实被删除 // 此处触发路由跳转,进而触发下方的observable }); this.router.events.pipe( filter((e: Event): e is NavigationEnd => e instanceof NavigationEnd) // 4种写法的差异如下 ); // getAsync实现 async getAsync<T>(key: string) { const result: GetResult = await Storage.get({ key }); return JSON.parse(result.value) as T; }
选项1、2:读取旧值
两者问题本质完全一致:switchMapTo的参数在管道初始化时就立即求值,直接调用了getAsync方法,这个调用时机远早于clear().then里的清存+路由跳转操作,此时拿到的就是清空前的旧值。后续不管多少次路由事件触发,switchMapTo都只会复用初始化时就已经生成的Promise/Observable结果,自然读不到清空后的值。选项3:正常读取新值
switchMap传入的是回调函数,这个函数只有上游的NavigationEnd事件触发时才会执行,此时clear操作已经执行完成,调用getAsync自然拿到的是清空后的空值。选项4:正常读取新值
使用了RxJS的defer操作符,它的作用是将内部Observable的创建逻辑延迟到被订阅的时候才执行。虽然switchMapTo的参数还是在管道初始化时就求值,但此时得到的是一个冷的defer Observable,只有当路由事件触发、switchMap订阅这个defer实例时,才会执行内部的getAsync调用,此时清存已经完成,自然能拿到最新值。
和事件循环的关联补充
管道初始化的代码属于同步执行逻辑,会在微任务clear().then执行前就跑完,进一步导致选项1、2中getAsync的调用时机早于清存操作,放大了问题的表现。
内容的提问来源于stack exchange,提问作者Itay
相关产品推荐
相关产品推荐

