RxJS Observable被订阅两次:subscribe内代码执行两次但仅调用一次
RxJS单次调用subscribe但回调执行两次的问题解决方案
问题根因分析
- 最常见的触发原因是Observable被多次订阅:即使你手动只写了一次subscribe,如果在前端框架模板中使用了
async管道绑定该Observable,相当于框架自动完成了一次额外订阅,两次订阅自然会触发两次回调 - 当前
post方法写法存在缺陷:from(fetch(...))的写法会在调用post方法时就立即执行fetch发起请求,而不是等到subscribe时才发起,不符合RxJS冷Observable的最佳实践 - 如果使用的是Angular/React/Vue的开发环境且开启了严格模式,开发环境下组件会被重复挂载一次,可能导致
doLogin方法被隐式调用两次 - Promise本身是多播的,基于
from转换Promise得到的Observable会保留多播特性:即使只发起一次请求,多次订阅也会收到相同的响应结果,触发多次回调
修复方案
第一步:优化post方法写法,改成标准的冷Observable
修改后的post方法只有在被订阅时才会发起请求,避免请求提前触发:
post(url, body): Observable<Response> { // 用defer包裹fetch调用,保证只有订阅时才执行fetch return defer(() => from(fetch(url, { method: 'POST', body: JSON.stringify(body), headers: { 'Accept': 'application/json, text/plain', 'Content-Type': 'application/json;charset=UTF-8' } }))) }
第二步:排查重复订阅问题
- 移除模板中对
stream$的async管道绑定,或者删掉手动的subscribe调用,二者选其一即可 - 在
doLogin方法第一行加打印日志,确认方法确实只被触发一次
第三步:多订阅场景兼容
如果业务逻辑确实需要多处订阅同一个请求的Observable,可以添加share操作符将其转为多播,无论多少次订阅只会触发一次请求、一次回调广播:
post(url, body): Observable<Response> { return defer(() => from(fetch(url, { method: 'POST', body: JSON.stringify(body), headers: { 'Accept': 'application/json, text/plain', 'Content-Type': 'application/json;charset=UTF-8' } }))).pipe(share()) }
严格模式验证
如果是开发环境严格模式导致的重复触发,生产环境打包后会自动消失,也可以临时关闭框架的严格模式验证是否该原因导致。
内容的提问来源于stack exchange,提问作者hugoboss
相关产品推荐
相关产品推荐

