V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
shakukansp
V2EX  ›  JavaScript

记一次 RXjs 中 Subject 的使用经历

  •  
  •   shakukansp · 2020-11-03 02:02:02 +08:00 · 2283 次点击
    这是一个创建于 1481 天前的主题,其中的信息可能已经有所发展或是发生改变。
    写项目的时候遇到一个场景
    inputA blur 的时候会触发自己的表单验证并发出一个请求,请求数据,填入 input C
    点击按钮 B,会触发 A 、C 的表单验证,并发送 A 、C 中的数据给服务端

    问题来了,需要保证 A 的异步完成之后再进行 B 的异步请求,于是第一时间想到用 rxjs 的 subject
    思路是这样:
    创建一个 subject D,
    按钮 B 订阅 D,subscribe 的 next 定义为 发送 A 、C 数据给服务端的异步操作
    这样 Input A 在异步获取 C 的回调中 触发 subject D 的 next 方法,把请求结果通过 subject 分发给 B
    B 再在 subscribe 中接收 D 分发来的值,把数据发送给服务端
    但是实操过程 中发现 B 的表单验证会慢于 A 的 next…… 也就是 A 已经请求完数据把数据推送给 D 了,但是 B 表单验证还没完…… 所以接收不到 A 传过来的值,无法继续发送请求

    无奈只好又创建了 subject E, 在 A 的请求发出去之前 订阅 B 的表单验证等待其完成,subscribe 的 next 为 A 请求 C 数据的过程……
    这样一来,B 就需要在验证完成之后 触发 E 的 next, 把表单验证完成这个事情告诉 A …… A 接到 B 验证成功的消息之后再通过 D 把 请求结果发给 B……

    好了,写完之后测试了一下,和预想中的结果一样,完美……
    ??
    那 A blur 的时候如果人没在点击 B,不就没法通知 A 表单验证完成可以继续了吗?
    所以需要加一个判断…… 判断 A 的 blur 事件的 relatedTarget 存不存在且是不是 B, 如果不存在或者不是 B 那么就由 A 自己通知自己完成请求 C 的值的过程。

    这下终于怎么测都没问题了。

    然后我发现直接 click 按钮 B 的时候 也发送 A 的请求,然后在 A 请求的回调里面进行 B 的后续请求即可
    只要在 A blur 的时候判断一下 relatedTarget 存不存在且是不是 B
    如果不存在或者不是 B 那么 A 就不发请求
    完事

    蛋疼。
    6 条回复    2020-11-03 10:51:08 +08:00
    chnwillliu
        1
    chnwillliu  
       2020-11-03 05:13:35 +08:00
    感觉不需要 Subject 吧。

    A blur 会触发验证并发请求,这个就它自己管好自己就 OK 。B click 需要检测当前有无正在 pending 的 A blur 引起的请求,有则等待,无则直接做自己该做的事。

    不追求完全函数编程,就可以把 A blur 发请求的 observable 用变量保存起来。

    大概这个意思:

    ```
    let pendingRequest = null;

    inputABlur$
    .pipe(
    switchMap(() => {
    pendingRequest = httpClient.get('/url', data).pipe(share());
    return pendingRequest;
    }),
    finallize(() => {
    pendingRequest = null;
    })
    )
    .subscribe();


    buttonBClick$
    .pipe(
    switchMap(value => {
    if (pendingRequest) {
    return pendingRequest.pipe(mapTo(value));
    }
    return of(value);
    }),
    switchMap(() => httpClient.get('/url-a-c', dataAC))
    )
    .subscribe();


    ```
    chnwillliu
        2
    chnwillliu  
       2020-11-03 05:21:19 +08:00
    你这里的表单验证具体是什么操作?不应该是同步的操作么?
    xuanbg
        3
    xuanbg  
       2020-11-03 07:41:05 +08:00
    在 A 的回调里面显示 B 按钮,这样就能保证 A 返回正确结果后 B 才变得可点击。凡是需要套娃的都可以套用这个方法。
    shakukansp
        4
    shakukansp  
    OP
       2020-11-03 09:19:04 +08:00
    @chnwillliu 一般是用户填完了 a 以后 blur,看到 c 框内的数据改变了,然后再去点 B

    但是现在测试的时候有粘贴到 a 以后直接点 B 的情况,这里需要处理 blur -> click 极短时间内的异步流程


    @xuanbg 确实,但是这个问题的场景就像上面说的,用户粘贴到 a 以后直接去点了 b,现在想想获取一开始设置 b 不可点击,C 有值的时候再允许点击比较好
    chnwillliu
        5
    chnwillliu  
       2020-11-03 10:25:09 +08:00 via Android
    @shakukansp blur 总是会优先于 click 触发的,所以 click 内部判断有无 blur 引起的 request 在 pending 就可以。

    不知道你的 A 框中的值不同会不会影响 C 框得到不一样的结果,如果会的话,C 有值再 enable B 按钮这种方案还是有问题。
    shakukansp
        6
    shakukansp  
    OP
       2020-11-03 10:51:08 +08:00
    @chnwillliu 确实不妥,我尝试一下你的方法
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2780 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 13:05 · PVG 21:05 · LAX 05:05 · JFK 08:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.