• Angular18 RXJS


    1 RX

      全称是 Reactive Extensions,它是微软开发并维护的基于 Reactive Programming 范式实现的一套工具库集合;RX结合了观察者模式、迭代器模式、函数式编程来管理事件序列

      RX官方文档:点击前往  

    2 RXJS

      RXJS就是RX在JavaScript层面上的实现;RxJS 是一个库,它通过使用 observable 序列来编写异步和基于事件的程序。它提供了一个核心类型 Observable,附属类型 (Observer、 Schedulers、 Subjects) 和受 [Array#extras] 启发的操作符 (map、filter、reduce、every, 等等),这些数组操作符可以把异步事件作为集合来处理。

      RXJS官方文档:点击前往

    3 RXJS中解决异步事件管理的一些基本概念

      3.1 Observable

        可观察对象:表示一个可调用的未来值或者事件的集合

        官方文档:点击前往

      3.2 Observer

        观察者对象:一个回调函数集合,它知道怎么去监听被可观察对象Observable发送的值

        官方文档:点击前往

      3.3 Subscription

        订阅:表示一个可观察对象Observable的执行,主要用于取消可观察对象Observable执行

        官方文档:点击前往

      3.4 Operators

        操作符:就是一些义函数式编程来处理可观察对象Observable,使用像 mapfilterconcatflatMap 等这样的操作符来处理集合。

        官方文档:点击前往

      3.5 Subject

        相当于 EventEmitter,并且是将值或事件多路推送给多个 Observer 的唯一方式。

        官方文档:点击前往  

      3.6 Schedulers

        用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeout 或 requestAnimationFrame 或其他。

        官方文档:点击前往

    4 简单实例

      技巧01:利用 jsbin 这个在线的JS编辑器作为测试编辑器,连接地址 -> 点击前往

      技巧02:本博文使用JS版本都是ES6

      4.1 单击按钮实例

        4.1.1 在HTML中通过script标签引入RXJS库 

    <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>

        4.1.2 在HTML中添加一个button标签   

    <button id="btn" >单击查看效果</button>

        4.1.2 通过JS实现RXJS编程   

    const btn = document.getElementById('btn');
    const btn$ = Rx
                 .Observable
                 .fromEvent(btn, 'click');
    btn$.subscribe(value => console.log('你点击了按钮哟'));

        代码解释:

          第一行:获取ID为“btn”的DOM节点

          第二行:将获取到的DOM节点的单机事件添加到一个可观察对象Observable的序列中

          第三行:订阅可观察对象Observable,当对应DOM节点的单机事件被触发时就会执行相应的操作(本实例是打印出一些信息)

        4.1.3 效果展示

          

        4.1.4 代码汇总

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
      
      <button id="btn" >单击查看效果</button>
     
      <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>
    </body>
    </html>
    HTML
    const btn = document.getElementById('btn');
    const btn$ = Rx
                 .Observable
                 .fromEvent(btn, 'click');
    btn$.subscribe(value => console.log('你点击了按钮哟'));
    ES6

      4.2 文本框输入实例

        4.2.1 在HTML中通过script标签引入RXJS库    

    <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>

        4.2.2 在HTML中添加一个input标签 

    <input id="test" type="text" placeholder="请输入一些信息" />

        4.2.3 通过JS实现RXJS编程  

    const test = document
                   .getElementById('test');
    const test$ = Rx
                    .Observable
                    .fromEvent(test, 'keyup')
                    .debounceTime(500)
                    .pluck('target', 'value');
    test$
      .subscribe(value => console.log(value));

          代码解释:

            fromEvent -> 将DOM节点的keyup事件添加到可观察对象Observable中

            debounceTime -> DOM节点的keyup事件触发后0.5秒后才推送

            pluck -> 获取触发事件DOM节点的value值

        4.2.4 效果展示

          

        4.2.5 代码汇总     

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
      
      <input id="test" type="text" placeholder="请输入一些信息" />
      
      <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>
    </body>
    </html>
    HTML
    const test = document
                   .getElementById('test');
    const test$ = Rx
                    .Observable
                    .fromEvent(test, 'keyup')
                    .debounceTime(500)
                    .pluck('target', 'value');
    test$
      .subscribe(value => console.log(value));
    ES6

     5 弹珠图

      要解释操作符是如何工作的,文字描述通常是不足以描述清楚的。许多操作符都是跟时间相关的,它们可能会以不同的方式延迟(delay)、取样(sample)、节流(throttle)或去抖动值(debonce)。图表通常是更适合的工具。弹珠图是操作符运行方式的视觉表示,其中包含输入 Obserable(s) (输入可能是多个 Observable )、操作符及其参数和输出 Observable 。

      

    6 可观察对象Observable

      技巧01:可观察对象Observable是数据的产生者,它会将数据以流的方式推送给观察者Observer;可以将可观察对象Observable看作是一个可以同步、异步返回多个值的函数

    const test$ = Rx.Observable
                      .create(observer => {
                        observer.next(1);
                        observer.next(2);    
                        observer.next(3);
                        setTimeout(() => observer.next('一秒钟后异步推送的数据'), 1000);
                        setTimeout(() => {
                          observer.next('2秒钟后异步推送的数据');
                          observer.complete();
                        }, 2000);
                      });

        代码解释:上面的代码中利用create操作符创建了一个Observable,该Observable一旦被订阅就会向Observer推送1、2、3,然后1秒钟后推送“一秒钟后异步推送的数据”,再过1秒钟后推送“2秒钟后异步推送的数据”,紧接着是完成数据流的推送

      技巧02:订阅Observable是Observable执行和发送值/事件给Observer最简单的方式;订阅Observable就类似于调用一个拥有多个返回值的函数

    const test$Sub = test$.subscribe(
                              value => console.log(value),
                              error => console.log(error),
                              () => console.log('数据推送完成')                        
                     );

        代码解释:上面的代码是订阅Observable的一个实例test$,订阅方法subscribe中的参数是有顺序的,其中第一个参数是位置参数用于处理数据,第二三个参数是可选参数,其中第二个参数用来处理错误,第三个参数用来处理数据推送完成后的一些操作;订阅后会返回一个Subscription对象,可以通过该对象来关闭正在执行的Observable

      技巧03:利用Subscription对象来关闭正在执行的Observable

    const test$ = Rx.Observable
                      .create(observer => {
    //                    const t =  setInterval(() => observer.next('hello'), 1000);
                        observer.next(1);
                        observer.next(2);    
                        observer.next(3);
                        const st01 = setTimeout(() => observer.next('一秒钟后异步推送的数据'), 1000);
                        const st02 = setTimeout(() => {
                          observer.next('2秒钟后异步推送的数据');
                          observer.complete();
                        }, 2000);
                        
                        return function unsubscribe() {
                          clearTimeout(st01);
                          clearTimeout(st02);
    //                       clearInterval(t);
                        }
                      });
    const test$Sub = test$.subscribe(
                              value => console.log(value),
                              error => console.log(error),
                              () => console.log('数据推送完成')                        
                     );
    setInterval(() => test$Sub.unsubscribe(), 1000);
    // test$Sub.unsubscribe();

        代码解释:使用create创建Observable的时候回返回一个函数unsubscribe,该函数用来定义如何清理执行的资源;订阅该Observable后就会返回一个Subscription对象,通过该Subscription对象来调用unsubscribe方法来取消正在执行的Observable

    7 观察者Observer

      Observer是Observable推送的值的消费者;Observer是一组回调函数的集合,每个回调函数对应一种 Observable 发送的通知类型:nexterror 和 complete。下面的示例是一个典型的观察者对象:

    var observer = {
      next: x => console.log('Observer got a next value: ' + x),
      error: err => console.error('Observer got an error: ' + err),
      complete: () => console.log('Observer got a complete notification'),
    };

      技巧01:使用Observer就必须将他们提供给Observable的subscirbe方法

      坑01:虽然所Observer是一个对象,但是在将Observer传给Observable的subscribe方法时只需要将对象中的内容传进去就可以啦,例如下面第一种方式就是错误的,第二种方式才是正确的

    test$.subscribe(
                              {
                                   value => console.log(value),
                                   error => console.log(error),
                                   () => console.log('数据推送完成')   
                              }                     
                     );  
    错误的使用方法
    test$.subscribe(
                              value => console.log(value),
                              error => console.log(error),
                              () => console.log('数据推送完成')                        
                     );

    8 操作符

      操作符是 Observable 类型上的方法,比如 .map(...).filter(...).merge(...),等等。当操作符被调用时,它们不会改变已经存在的 Observable 实例。相反,它们返回一个新的 Observable ,它的 subscription 逻辑基于第一个 Observable。(操作符是函数,它基于当前的 Observable 创建一个新的 Observable。这是一个无副作用的操作:前面的 Observable 保持不变)

      操作符本质上是一个纯函数 (pure function),它接收一个 Observable 作为输入,并生成一个新的 Observable 作为输出。订阅输出 Observalbe 同样会订阅输入 Observable 。

      8.1 小案例之mapTo

        每次源 Observble 发出值时,都在输出 Observable 上发出给定的常量值;接收常量 value 作为参数,并每当源 Observable 发出值时都发出这个值。换句话说, 就是忽略实际的源值,然后简单地使用这个发送时间点以知道何时发出给定的 value

        

        8.1.1 效果展示

          

        8.1.2 代码汇总  

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
      <button id="btn" type="button">点击测试</button>
      <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>
    </body>
    </html>
    HTML
    // 根据ID获取DOM对象
    const btn = document.getElementById("btn");
    
    // 将对应DOM对象的单击事件变成事件流
    const btn$ = Rx.Observable
                    .fromEvent(btn, "click")
                    .mapTo('ello');
    
    // 订阅事件流
    btn$.subscribe(value => console.log(value));
    ES6

      8.2 创建类操作符

        8.2.1 interval

    public static interval(period: number, scheduler: Scheduler): Observable

          创建一个 Observable ,该 Observable 使用指定的 IScheduler ,并以指定时间间隔发出连续的数字。

          

          坑01:interval产生的第一个数并不是立即就产生的,而是当第一个时间间隔过后才会推送第一个数字,而且推送的数字是从0开始的自增数字

    const test$ = Rx.Observable
                    .interval(1000);
    test$.subscribe(value => console.log(value));

          技巧01:官方文档 => 点击前往

        8.2.2 timer

    public static timer(initialDelay: number | Date, period: number, scheduler: Scheduler): Observable

          根据给定的时间间隔自增的产生数字

          参数解释:

            参数01:产生第一个数字的间隔时间

            参数02:产生下一个数字的时间间隔(可选)

            参数03:调度器,用来调度值的发送, 提供“时间”的概念  (可选,默认值: async)

          坑01:如果只给定第一个参数,那么会在时间间隔到达后推送数字0,然后就不在继续推送数据啦

          技巧01:其实timer的用法和interval非常想,都是自动产生自动的数字

          

          

    const test$ = Rx.Observable
                    .timer(1000, 1000)
                    .take(3)
                    .map(value => value * 10);
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

      8.3 合并类操作符

        8.3.1 combineLatest

          组合多个 Observables 来创建一个 Observable ,该 Observable 的值根据每个输入 Observable 的最新值计算得出的;组合多个 Observables 来创建一个 Observable ,该 Observable 的值根据每个输入 Observable 的最新值计算得出的。

          

          技巧01:每个Observables都必须有值,而且他们其中必须有一个的值更新后才会执行combineLatest

          》效果展示

            

          》代码汇总

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
      <input type="number" id="weight" placeholder="请输入体重" />
      <span>Kg</span>
      <br />
      <input type="number" id="height" placeholder="请输入身高" />
      <span>cm</span>
      
      <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js"></script>
    </body>
    </html>
    HTML
    const weight = document.getElementById('weight');
    const height = document.getElementById('height');
    
    const weight$ = Rx.Observable
                      .fromEvent(weight, 'keyup')
                      .debounceTime(500)
                      .pluck('target', 'value');
    const height$ = Rx.Observable
                      .fromEvent(height, 'keyup')
                      .debounceTime(500)
                      .pluck('target', 'value');
    
    const bmi$ = Rx.Observable
                   .combineLatest(
                     weight$, 
                     height$, 
                     (w, h) => {
                       const str = '成人BMI数值为:';
                       const bmi = w / (h * h / 100 / 100);
                       if (bmi < 18.5) {
                         return str + bmi + ' -> 过轻';
                       } else {
                         if (bmi < 23.9) {
                           return str + bmi + ' -> 正常';
                         } else {
                           if (bmi < 27) {
                             return str + bmi + ' -> 过重';
                           } else {
                             if (bmi < 32) {
                               return str + bmi + ' -> 肥胖';
                             } else {
                               return str + bmi + ' -> 非常肥胖';
                             }
                           }
                         }
                       }
                      
                     }
                   );
    
    // 过轻:低于18.5
    // 正常:18.5-23.9
    // 过重:24-27
    // 肥胖:28-32
    // 非常肥胖, 高于32
    // weight$.subscribe(value => console.log(value));
    // height$.subscribe(value => console.log(value));
    bmi$.subscribe(value => console.log(value));
    ES6

        8.3.2 merge

    public merge(other: ObservableInput, concurrent: number, scheduler: Scheduler): Observable

          将两个数据源的数据合并到一起

          

          

    const test01$ = Rx.Observable
                      .create(observer => {
                        observer.next(1);
                        setTimeout(() => {
                          observer.next(2);
                        }, 1000);
                        observer.next(3);
                      });
    const test02$ = Rx.Observable
                      .create(observer => {
                        observer.next(4);
                        observer.next(5);
                        observer.next(6);
                      });
    const test$ = Rx.Observable
                    .merge(test01$, test02$);
    
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    View Code

        8.3.3 startWith

    public startWith(values: ...T, scheduler: Scheduler): Observable

          向原数据源添加一个数据作为第一个推送的数据

          

          

    const test01$ = Rx.Observable
                      .create(observer => {
                        observer.next(1);
                        setTimeout(() => {
                          observer.next(2);
                        }, 1000);
                        observer.next(3);
                      })
                      .startWith('HELLO');
    
    test01$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    View Code

        8.3.4 zip

          根据顺序将两个数据源中的数据进行组合

           技巧01:如果一个数据源中有3个数据,另外一个数据源中有4个数据,那么只会将这两个数据源中前面推送的3个数据进行组合

          

    const test01$ = Rx.Observable
                      .create(observer => {
                        observer.next(1);
                        setTimeout(() => {
                          observer.next(2);
                        }, 1000);
                        observer.next(3);
                      });
    const test02$ = Rx.Observable
                      .create(observer => {
                        observer.next('a');
                        observer.next('b');
                        observer.next('c');
                        observer.next('d');
                      });
    
    const test$ = Rx.Observable
                    .zip(test01$, test02$);
    
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

      

      8.4 其他操作符

        8.4.1 take

    public take(count: number): Observable<T>

          只会推送原数据流中前面的count个数据      

          

    const test$ = Rx.Observable
                    .interval(1000)
                    .take(3);
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );

          

        8.4.2 map

    public map(project: function(value: T, index: number): R, thisArg: any): Observable<R>

          对原数据源的每个值做同样的处理

           

           

    const test$ = Rx.Observable
                    .interval(1000)
                    .take(3)
                    .map(value => value * 10);
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

         8.4.3 filter 

    public filter(predicate: function(value: T, index: number): boolean, thisArg: any): Observable

          对原数据源的数据进行过滤

          参数解释:

            参数01:过滤操作函数,操作函数中第一个参数代表正在过滤的数据,第二个参数代表原数据源序列的索引(注意:索引是从0开始的,用处还问弄清楚)???

            参数02:可选参数,用来决定 predicate 函数中的 this 的值

          

           

          

    const test$ = Rx.Observable
                    .timer(1000, 1000)
                    .take(5)
                    .map(value => value * 10)
                    .filter((value) => value > 10);
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

        8.4.4 first

    public first(predicate: function(value: T, index: number, source: Observable<T>): boolean, resultSelector: function(value: T, index: number): R, defaultValue: R): Observable<T | R>

          只推送原数据源中的第一个值或者第一个满足条件的值

          技巧01:如果first不携带任何参数,那么将会推送原数据源中的第一个值(和take(1)的效果相同);如果携带一个返回boolean的方法,那么将返回满足该方法的第一个数据

          

          

    const test$ = Rx.Observable
                    .timer(1000, 1000)
                    .take(5)
                    .map(value => value * 10)
    //                 .first();
                    .first((value) => value > 10);
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

         8.4.5 last 

    public last(predicate: function): Observable

          只推送原数据源中的最后一个数据

          技巧01:如果last不指定参数时,就会默认只推送最后一个数据;如果指定了一个返回boolean类型的方法作为参数,那么就会返回最后一个满足条件的数据

           

          

    const test$ = Rx.Observable
                    .interval(1000)
                    .take(5)
                    .last();
    //                 .last(value => value < 3 );
    
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

        8.4.6 skip  

    public skip(count: Number): Observable

          在推送数据时,跳过前面count个数据

          

          

    const test$ = Rx.Observable
                    .interval(1000)
                    .take(5)
                    .skip(2);
    
    test$.subscribe(
            value => console.log(value),
            error => console.log(error),
            () => console.log('完成')
          );
    ES6

        8.4.7 scan

    public scan(accumulator: function(acc: R, value: T, index: number): R, seed: T | R): Observable<R>

          对原数据源序列进行依次累加,每累加玩完一次就会推送一个结果

          

          参数解释:

            参数01:一个累加方法,该方法的第一个参数代表累加结果,第二个参数代表当前累加值,该累加值随便用一个变量表示即可

            参数02:可选参数,累加器的初始值,如果不填就会默认将原数据源的第一个数据作为初始累加值

          

    const b = 100;
    const test01$ = Rx.Observable
                      .interval(1000)
                      .take(5)
                      .scan((acc, a) => acc + a, b);
    
    test01$.subscribe(
              value => console.log(value),
              error => console.log(error),
              () => console.log('完成')
            );
    ES6

         8.4.8 reduce

    public reduce(accumulator: function(acc: R, value: T, index: number): R, seed: R): Observable<R>

          对原数据源中的数据进行累加,最后只将累加的结果推送

          参数解释:

            参数01:第一个参数是一个累加方法,该累加方法接受两个参数,第一个参数代表累加结果,第二个参数代表当前累加值

            参数02:可选参数,该参数如果指定了就会被用作累加的初始值

          

          

    const b = 100;
    const test01$ = Rx.Observable
                      .interval(1000)
                      .take(5)
                      .reduce((acc, a) => acc + a, b);
    
    test01$.subscribe(
              value => console.log(value),
              error => console.log(error),
              () => console.log('完成')
            );
    ES6

        8.4.9 debounceTime

    public debounceTime(dueTime: number, scheduler: Scheduler): Observable

          当没有新的数据添加到原数据源时,延迟发送原数据源中的数据

          

          

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
    
      
      <input type='text' id="test" placeholder="测试文本框" />
    
      <script src="https://unpkg.com/@reactivex/rxjs@5.0.3/dist/global/Rx.js">
      </script>
    </body>
    </html>
    HTML
    const test = document.getElementById('test');
    const test$ = Rx.Observable
                    .fromEvent(test, 'keyup')
                    .debounceTime(500)
                    .pluck('target', 'value');
    
    test$.subscribe(
           value => console.log(value),
           error => console.log(error),
           () => console.log('完成')
         );
    ES6

         8.4.10 distinct

    public distinct(keySelector: function, flushes: Observable): Observable

          去除掉原数据源中重复的数据

          技巧01:如果distinct方法提供了keySelector方法,就会先利用keySelector方法对原数据源你的数据进行处理后在进行去重操作

          官方文档 => 点击前往

            

    const test01$ = Rx.Observable
                      .create(observer => {
                        observer.next(1);
                        observer.next(2);
                        observer.next(3);
                        observer.next(1);
                        observer.next(2);
                        observer.next(3);
                      })
                      .distinct();
    test01$.subscribe(value => console.log(value));
    ES6

        8.4.11 distinctUntilChanged

    public distinctUntilChanged(compare: function): Observable

          去除掉原数据源相邻数据的重复数据

           

    const test01$ = Rx.Observable
                      .create(observer => {
                        observer.next(1);
                        observer.next(2);
                        observer.next(3);
                        observer.next(3);
                        observer.next(3);
                        observer.next(1);
                        observer.next(2);
                        observer.next(3);
                      })
                      .distinctUntilChanged();
    test01$.subscribe(value => console.log(value));
    ES6
  • 相关阅读:
    HttpSession
    查看端口被哪个进程占用了
    变体类型 Variant VARIANT
    BDE View not exists
    c++builder 解压缩
    nginx的allow和deny配置
    linux下如何启动nginx?
    java如何发起一次http的post请求?
    mysql如何用sql添加字段如何设置字符集和排序规则
    设置Tomcat的UTF-8编码
  • 原文地址:https://www.cnblogs.com/NeverCtrl-C/p/8338265.html
Copyright © 2020-2023  润新知