前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出 RxJS 之 函数响应式编程

深入浅出 RxJS 之 函数响应式编程

作者头像
Cellinlab
发布2023-05-17 20:15:50
1.1K0
发布2023-05-17 20:15:50
举报
文章被收录于专栏:Cellinlab's BlogCellinlab's Blog

# Hello RxJS

使用 jQuery 实现时间感应用。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <div>测试对时间的感觉</div>
    <button id="hold-me">按住 1s 后松开</button>
    <div>你的时间:<span id="hold-time"></span>ms</div>
    <div id="rank"></div>
  </div>
  <script
  src="https://code.jquery.com/jquery-1.12.4.js"
  integrity="sha256-Qw82+bXyGq6MydymqBxNPYTaUXXq7c8v3CwiYwLLNXU="
  crossorigin="anonymous"></script>
  <script>
    let startTime;
    $('#hold-me').mousedown(function() {
      startTime = new Date();
    });
    $('#hold-me').mouseup(function () {
      if (startTime) {
        const elapsedTime = new Date() - startTime;
        startTime = null;
        $('#hold-time').text(elapsedTime);
        $.ajax(`https://timing-sense-score-board.herokuapp.com/score/${elapsedTime}`)
          .done(function (data) {
            $('#rank').text(`你超过了 ${data.rank}% 的人`);
          });
      }
    });
  </script>
</body>
</html>

使用 RxJS 实现:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <div>测试对时间的感觉</div>
    <button id="hold-me">按住 1s 后松开</button>
    <div>你的时间:<span id="hold-time"></span>ms</div>
    <div id="rank"></div>
  </div>
  <script src="https://unpkg.com/rxjs@5.4.2/bundles/Rx.min.js"></script>
  <script>
    const holdMeBtn = document.querySelector('#hold-me');
    const mouseDown$ = Rx.Observable.fromEvent(holdMeBtn, 'mousedown');
    const mouseUp$ = Rx.Observable.fromEvent(holdMeBtn, 'mouseup');

    const holdTime$ = mouseUp$.timestamp().withLatestFrom(
      mouseDown$.timestamp(),
      (up, down) => up.timestamp - down.timestamp
    );

    holdTime$.subscribe(ms => {
      document.querySelector('#hold-time').innerText = ms;
    });

    holdTime$.flatMap(ms => Rx.Observable.ajax(`https://timing-sense-score-board.herokuapp.com/score/${ms}`))
      .map(e => e.response)
      .subscribe(data => {
        document.querySelector('#rank').innerText = `你超过了 ${data.rank}% 的人`;
      });
  </script>
</body>
</html>

RxJS 世界中有一种特殊对象——“流”,也可以叫“数据流”或“Observable对象”。

代表“流”的变量标识符,都是以 $ 结尾。

上面 mouseDown 和 mouseUp 都是数据流,分别代表按钮上的 mousedown 事件和 mouseup 事件集合,不光包含已经发生的事件,还包含没有发生的鼠标事件。对数据流一视同仁,这就是数据流的妙处。

“流”可以通过多种方法创造出来,mouseDown 和 mouseUp 通过 fromEvent 函数从网页的 DOM 元素中获得,holdTime 这个流则是通过 mouseDown 和 mouseUp

流对象中“流淌”的是数据,而通过 subscribe 函数可以添加函数对数据进行操作,上面的代码中,对 holdTime$ 对象有两个 subscribe 调用,一个用来更新 DOM,另一个用来调用 API 请求。

在 jQuery 的实现中,有被交叉访问的变量(startTime),两个不同函数的逻辑相互关联,稍有不慎就会引发 bug ,代码看起来就是一串指令的组合;在RxJS的代码中,没有这样纠缠不清的变量,会发现所有的变量其实都没有“变”,赋值时是什么值,就会一直保持这些值,代码是一个一个函数,每个函数只是对输入的参数做了响应,然后返回结果。

RxJS 引用了两个重要的编程思想:

  • 函数式
  • 响应式

# 函数式编程

# 什么是函数式编程

强调使用函数来解决问题的一种编程方式。函数式编程对函数的使用有一些特殊的要求,这些要求包括以下几点:

  • 声明式
  • 纯函数
  • 数据不可变性

从语言角度讲,JavaScript 不算一个纯粹意义上的函数式编程语言,但是,JavaScript 中的函数有第一公民的身份,因为函数本身就是一个对象,可以被赋值给一个变量,可以作为参数传递,由此可以很方便地应用函数式编程的许多思想。

把函数式编程看作一种编程思想,即使语言本身不支持一些特性,依然可以应用这样的编程思想,用于提高代码的质量。

JavaScript 如何满足函数式编程的特性需要:

声明式

命名式编程

代码语言:javascript
复制
function double(arr) {
  const results = [];
  for (let i = 0; i < arr.length; i++) {
    results.push(arr[i] * 2);
  }
  return results;
}

function addOne (arr) {
  const results = [];
  for (let i = 0; i < arr.length; i++) {
    results.push(arr[i] + 1);
  }
  return results;
}

  • 世界上很多问题都有相似的模式,命名式编程会使出现很多重复代码

声明式编程

把一个数组映射成另一个数组

代码语言:javascript
复制
function double (arr) {
  return arr.map(function (item) {
    return item * 2;
  });
}
function addOne(arr) {
  return arr.map(function (item) {
    return item + 1;
  });
}

进一步简化

代码语言:javascript
复制
const double = arr => arr.map(item => item * 2);
const addOne = arr => arr.map(item => item + 1);

纯函数

  • 纯函数要满足的条件
    • 函数的执行过程完全由输入参数决定,不会受除参数之外的任何数据影响
    • 函数不会修改任何外部状态,比如修改全局变量或传入的参数对象
  • 好处
    • 纯函数让代码更加简单,从而更加容易维护,更加不容易产生 bug
    • 非常容易写单元测试的
      • TDD 的难以推行很大原因是很多项目不遵守函数式编程规范
      • 如果被测函数都是纯函数,单元测试可以轻松达到 100% 的代码覆盖率。
  • 可能导致函数不纯的原因
    • 改变全局变量的值
    • 改变输入参数引用的对象
    • 读取用户输入,比如调用了 alert 或者 confirm 函数
    • 抛出一个异常
    • 网络输入/输出操作,比如通过 AJAX 调用一个服务器的 API
    • 操作浏览器的 DOM
  • 本质:做的事情是输入参数到返回结果的一个映射,不要产生副作用

数据不可变

  • 需要数据状态发生改变时,保持原有数据不变,产生一个新的数据来体现这种变化
  • 不可改变的数据就是 Immutable 数据,它一旦产生,就可以肯定它的值永远不会变,这非常有利于代码的理解

# 函数式编程和面向对象编程的比较

简单说来,面向对象的方法把状态的改变封装起来,以此达到让代码清晰的目的;而函数式编程则是尽量减少变化的部分,以此让代码逻辑更加清晰。

面向对象的思想是把数据封装在类的实例对象中,把数据藏起来,让外部不能直接操作这些对象,只能通过类提供的实例方法来读取和修改这些数据,这样就限制了对数据的访问方式。对于毫无节制任意修改数据的编程方式,面向对象无疑是巨大的进步,因为通过定义类的方法,可以控制对数据的操作。

但是,面向对象隐藏数据的特点,带来了一个先天的缺陷,就是数据的修改历史完全被隐藏了。有人说,面向对象编程提供了一种持续编写烂代码的方式,它让你通过一系列补丁来拼凑程序。

函数式编程中,倾向于数据就是数据,函数就是函数,函数可以处理数据,也是并不像面向对象的类概念一样把数据和函数封在一起,而是让每个函数都不要去修改原有数据(不可变性),而且通过产生新的数据来作为运算结果(纯函数)。

# 响应式编程

# Reactive Extension

Reactive Extension,也叫 ReactiveX,或者简称 Rx,指的是实践响应式编程的一套工具。

An API for asynchronous programming with observable streams.

Rx(包括RxJS)诞生的主要目的虽然是解决异步处理的问题,但并不表示 Rx 不适合同步的数据处理,实际上,使用 RxJS 之后大部分代码不需要关心自己是被同步执行还是异步执行,所以处理起来会更加简单。

# RxJS 是否是函数响应式编程

FRP 包含两个重要元素:

  • 指称性(denotative)
  • 临时的连续性(temporally continuous)

正统 FRP 认为,一个系统如果能被称为 FRP,除了要有 FunctionalReactive 的特点,还必须要能够支持两个事件可以“同时发生”,这就是指称性的要求。总之,按照正统 FRP 的说法,你的系统只有 FunctionalReactive,不能说自己是 FRP。

包括 RxJS 在内的 Rx,到底算不算 FRP ?按照正统 FRP 的观点,Rx 不算,因为 Rx 不满足指称性的要求,在 Rx 的所有实现中,都存在一个局限,就是当两个“流”合并的时候,不能按照 FRP 那样严格处理同时发生的事件。

# 函数响应式编程的优势

RxJS 模型的特点:

  • 数据流抽象了很多现实问题
    • 网页 DOM 的事件,可以看作为数据流
    • 通过 WebSocket 获得的服务器端推送消息可以看作是数据流
    • 通过 AJAX 获得服务器端的数据资源也可以看作是数据流,虽然这个数据流中可能只有一个数据
    • 网页的动画显示当然更可以看作是一个数据流
  • 擅长处理异步操作
    • 对数据采用“推”的处理方式,当一个数据产生的时候,被推送给对应的处理函数,这个处理函数不用关心数据是同步产生的还是异步产生的,这样就把开发者从命令式异步处理的枷锁中解放了出来
  • 把复杂问题分解成简单问题的组合
    • 数据流可能包含复杂的功能,但是可以分解成很多小的部分来实现,实现某一个小功能的函数就是操作符
    • 可以说,学习 RxJS 就是学习如何组合操作符来解决复杂问题
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/7/17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • # Hello RxJS
  • # 函数式编程
    • # 什么是函数式编程
      • # 函数式编程和面向对象编程的比较
      • # 响应式编程
        • # Reactive Extension
        • # RxJS 是否是函数响应式编程
        • # 函数响应式编程的优势
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档