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

响应式编程是一种思想,是一种模式,就像 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 (
    
); }

解释说明:

  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实现真正双击事件流)