美文网首页
响应式编程实战——新版RxJS实现真正双击事件流

响应式编程实战——新版RxJS实现真正双击事件流

作者: du1dume | 来源:发表于2020-05-30 06:21 被阅读0次

    响应式编程是一种思想,是一种模式,就像 if else 和 for while 一样,是所有编程语言的基本单元。为什么会这样类比?因为现在基本所有的编程语言都有了自己的 Rx 实现,比如 RxJava, Rx.Net, RxJS, RxGo, RxDart等等,至少我们常用的语言都有自己的 Rx。再举个例子,比如斐波那契数列的实现和编程语言无关对么?不同点就是各语言的 if 语句、for 循环语句等等的写法有不同。Rx 也一样,比如我使用 RxJS 实现了按钮的双击操作,我可以把代码拷贝到 Java 中,修改一下操作符的名称,使用方式就好了。

    响应式编程同样也是实现组合式编程和积木式编程的最佳实践。使用的越多,你才能体会响应式编程的强大和美妙之处。理论是根基,实战才有价值,下面我们就来看看如何用 RxJS 实现按钮的双击效果。

    虽然 DOM 中已经有了双击事件类型,作为练习,让我们假设只有单击事件可以使用。

    如果不使用 RxJS 我们将会怎么做?

    1. counter 变量计数
    2. addEventListener 注册单击事件
    3. setTimeout 来判断时间间隔。

    让我们看看 RxJS 如何实现。我们知道 Rx 是事件流,单击事件流可以想象成从一根管子,单击事件可以想象成一个珠子,一发生单击事件,这个珠子就从管子的一头流入,从另一头流出。像下面这样:

    e----

    -e---

    --e--

    ---e-

    ----e

    下面看看怎么用 RxJS 实现这根管子:

    如何使用单击事件流实现双击事件流呢?管子好说,关键是里面的珠子。这里要用到 4 个 RxJS 操作符。先简要说下操作符。我觉得操作符可以类比为中间件,拦截函数等等,后台同学好理解,前台同学不理解的话可以去看看 koa 框架。操作符接收一根管子,监控管子里的珠子并做完操作,然后重新生成一根管子,里面存放操作完的珠子。

    throttleTime:throttle 的中文翻译是节流阀的意思。它的参数是一段时间。它的作用在时间参数范围内的珠子可以过,时间一到阀门就关上了,谁也过不去了。当第一个珠子经过它时,它就开始计时。

    buffer:它的参数是一个事件流或者说管子。它的作用是收集这根管子流出的珠子,放进一个袋子里,这个袋子就是数组。

    map:对经过它的珠子做处理。

    filter:对经过它的珠子做过滤,满足要求的珠子才能过去。

    下面是实现:

    import React, { useRef, useEffect } from "react";
    
    import { fromEvent } from "rxjs";
    import { buffer, filter, map, throttleTime } from "rxjs/operators";
    
    // throttleTime 操作符的第二个参数,其实默认值也是 async,那为什么还要传入呢?因为假如你有三个参数,传入了第一个和第三个,第二个就必须传了,js 无法忽略中间的参数不填。
    import { async } from "rxjs/internal/scheduler/async"; 
    
    export default function App() {
      const btnRef = useRef(null);
    
      // 这个是 throttleTime 操作符的第三个参数,默认值 leading 为 true,trailing 为 false。
      const throttleConfig = {
        leading: false,
        trailing: true
      }
    
      useEffect(() => {
        const btnClick$ = fromEvent(btnRef.current, "click");
        
        const throttled$ = btnClick$.pipe(throttleTime(250, async ,throttleConfig))
    
        const btnDblClick$ = btnClick$.pipe(
          buffer(throttled$),
          map(arr => arr.length),
          filter(len => len === 2),
        );
    
        const subscription = btnDblClick$.subscribe(() => {
          console.log("双击啦");
        });
        return () => {
          subscription.unsubscribe();
        };
      });
    
      return (
        <div className="App">
          <button ref={btnRef}>我是一个按钮</button>
        </div>
      );
    }
    

    解释说明:

    1. 快速地在按钮上点击两次,将产生两个珠子,也就是 btnClick$ 事件流里面有两个 click 事件。
    2. 我们把这根管子交给了 buffer 操作符,然而 buffer 对这根管子不感兴趣,它需要另外一个管子;这另外一根管子也是基于单击事件流生成的,只不过加了个阀门,里面有个定时器,当你第一次 click 的时候,定时器开始计时,计时期间的 click 事件都可以进到管子里;当定时器停止时,click 事件将无法进到管子里。定时器归零,直到你又一次 click 再开始计时。
    3. 我们把上一步的管子交给了 buffer,buffer 把这根管子里的珠子放进袋子,也就是数组中。比如交给 buffer 之前的管子里有两个珠子,交给 buffer 之后,buffer 把这俩数字放进了袋子,放进了一根新管子。
    4. 这一步 map 操作符拿到了管子,它算出袋子里的珠子数量,放到另一根新管子里,新管子里放的就是珠子的数量。
    5. filter 操作符拿到前面传过来的管子,它对里面的数字有意见,不等于 2 的都扔了,把等于 2 的放进了另一根管子,传递下去。
    6. 这一步没有操作符了,因此 filter 返回的管子给了 btnDblClick$ 变量保存。

    综上,只要 btnDlbClick$ 流出的事件,就是双击事件,明白了吗?不明白请关注微信公众号“读一读我”哦。

    相关文章

      网友评论

          本文标题:响应式编程实战——新版RxJS实现真正双击事件流

          本文链接:https://www.haomeiwen.com/subject/osaqzhtx.html