Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料

31、Scheduler (调度器)

RxJS 中的调度器 ( Schedulers ) 是用来控制事件发出的顺序和速度的(发送给观察者的)。

const a$ = Rx.Observable.of(1, 2);
const b$ = Rx.Observable.of(10);
                    
const c$ = Rx.Observable.combineLatest(a$, b$, (a, b) => a + b);
                    
c$.subscribe(c => console.log(c));

这里写图片描述
默认情况下,RxJS 使用所谓的递归调度器。下面是它的工作原理:

  1. c$ 被订阅
  2. combineLatest 的第一个输入流 a$ 被订阅
  3. a$ 发出值 1
  4. combineLatest 将 1 作为 a$ 的最新值进行保存
  5. a$ 发出值 2
  6. combineLatest 将 2 作为 a$ 的最新值进行保存
  7. combineLatest 的第二个输入流 b$ 被订阅
  8. b$ 发出值 10
  9. combineLatest 将 10 作为 b$ 的最新值进行保存
  10. combineLatest 现在同时拥有了 a$ 和 b$ 的值,因此它发出值 2 + 10

发出的顺序为 1, 2, 10。

咱们在 a$ 上使用 asap 调度器来让其“慢下来”:

const a$ = Rx.Observable.from([1, 2], Rx.Scheduler.asap); // 新代码
const b$ = Rx.Observable.of(10);

const c$ = Rx.Observable.combineLatest(a$, b$, (a, b) => a + b);

c$.subscribe(c => console.log(c))

这里写图片描述

  1. c$ 被订阅
  2. combineLatest 的第一个输入流 a$ 被订阅
  3. combineLatest 的第二个输入流 b$ 被订阅
  4. b$ 发出值 10
  5. combineLatest 将 10 作为 b$ 的最新值进行保存
  6. a$ 发出值 1
  7. combineLatest 将 1 作为 a$ 的最新值进行保存
  8. combineLatest 现在同时拥有了 a$ 和 b$ 的值,因此它发出值 1 + 10
  9. a$ 发出值 2
  10. combineLatest 将 2 作为 a$ 的最新值进行保存
  11. combineLatest 发出值 2 + 10

发出的顺序为 10, 1, 2。

咱们可以为 b$ 也自定义调度器:

const a$ = Rx.Observable.from([1, 2], Rx.Scheduler.asap);

const b$ = Rx.Observable.from([10], Rx.Scheduler.asap); // 新代码

const c$ = Rx.Observable.combineLatest(a$, b$, (a, b) => a + b);

c$.subscribe(c => console.log(c));
  1. c$ 被订阅
  2. combineLatest 的第一个输入流 a$ 被订阅
  3. combineLatest 的第二个输入流 b$ 被订阅
  4. a$ 发出值 1
  5. combineLatest 将 1 作为 a$ 的最新值进行保存
  6. b$ 发出值 10
  7. combineLatest 将 10 作为 b$ 的最新值进行保存
  8. combineLatest 现在同时拥有了 a$ 和 b$ 的值,因此它发出值 1 + 10
  9. a$ 发出值 2
  10. combineLatest 将 2 作为 a$ 的最新值进行保存
  11. combineLatest 发出值 2 + 10

发出的顺序为 1, 10, 2。

调度器还可以让事件的发出变得更快,同时保持发出的顺序不变。例如,RxJS 的 TestScheduler 可以使 Observable.interval(1000).take(10) 被订阅时进行同步执行,而不需要花费10秒钟来完成:

Rx.Observable.interval(1000, new Rx.TestScheduler()).take(10)

31、Scheduler 调度器类型

调度器 目的
null 不传递任何调度器的话,会以同步递归的方式发送通知。用于定时操作或尾递归操作。
Rx.Scheduler.queue 当前事件帧中的队列调度(蹦床调度器)。用于迭代操作。
Rx.Scheduler.asap 微任务的队列调度,它使用可用的最快速的传输机制,比如 Node.js 的 process.nextTick() 或 Web Worker 的 MessageChannel 或 setTimeout 或其他。用于异步转换。
Rx.Scheduler.async 使用 setInterval 的调度。用于基于时间的操作符。

这是官网给出的类型,大家看看就行。

32、Scheduler 调度器的使用

对于返回有限和少量消息的 observable 的操作符,RxJS 不使用调度器,即 null 或 undefined 。对于返回潜在大量的或无限数量的消息的操作符,使用 queue 调度器。对于使用定时器的操作符,使用 asap或aysnc 调度器。

静态创建操作符通常可以接收调度器作为参数。
Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第1张图片
使用 subscribeOn 来调度 subscribe() 调用在什么样的上下文中执行。

使用 observeOn 来调度发送通知的的上下文。

实例操作符可能会接收调度器作为参数。

像 bufferTime、debounceTime、delay、auditTime、sampleTime、throttleTime、timeInterval、timeout、timeoutWith、windowTime 这样时间相关的操作符全部接收调度器作为最后的参数,并且默认的操作是在 Rx.Scheduler.async 调度器上。

注意,cache 和 publishReplay 都接收调度器是因为它们使用了 ReplaySubject 。ReplaySubjects 的构造函数接收一个可选的调度器作为最后的参数,因为 ReplaySubject 可能会处理时间,这只在调度器的上下文中才有意义。默认情况下,ReplaySubject 使用 queue 调度器来提供时钟。

33、RxJs实例1——变量赋值

var a, b = 1, c =2;
a = b + c;
console.log('b=' + b);
console.log('c=' + c);
console.log('a=' + a);
b = 3;
c = 2;

console.log('a=' + a);

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第2张图片
这段中,我们如果想让a输出为5,就是当b和c二次赋值后a的值是不会自动更新为5的。我们称这种方式为命令式编程

var b$ = Rx.Observable.from([1, 3]);
var c$ = Rx.Observable.from([2, 2]);

var a$ = Rx.Observable.zip(b$, c$, (b, c) => {
    console.log('b=' + b);
    console.log('c=' + c);

    return b + c;

});

a$.subscribe(a => console.log('a=' + a));

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第3张图片
这段代码a的值就变化了,我们称这种方式为响应式编程

34、RxJs实例2——体质指数(BMI)

BMI:体重公斤数除以身高米数平方得出的数字。

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32



  
  


  Weight:  kg
  
Height: cm
Your BMI is
let weight = document.getElementById('weight'); let height = document.getElementById('height'); let bmi = document.getElementById('bmi'); let weight$ = Rx.Observable .fromEvent(weight, 'input') .pluck('target', 'value'); let height$ = Rx.Observable .fromEvent(height, 'input') .pluck('target', 'value'); let bmi$ = Rx.Observable .combineLatest(weight$, height$, (w, h) => w/(h*h/100/100)); bmi$.subscribe(b => bmi.innerHTML=b);

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第4张图片

35、RxJs实例3——事件的运用




  
  
  JS Bin
  






let todo = document.getElementById('todo');
let input$ = Rx.Observable.fromEvent(todo, 'keyup');
input$.subscribe(input => console.log(input.target.value));

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第5张图片
如果我一直按着某个键不放会怎么样呢?
Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第6张图片

let todo = document.getElementById('todo');
let input$ = Rx.Observable.fromEvent(todo, 'keyup');
input$
  .filter(ev=>ev.keyCode===32)
  .subscribe(ev=>console.log(ev.target.value));

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第7张图片




  
  
  JS Bin
  






let todo = document.getElementById('todo');
let input$ = Rx.Observable.fromEvent(todo, 'keyup');
input$
  .map(ev=>ev.target.value*10)
  .subscribe(value=>console.log(value));

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第8张图片

let todo = document.getElementById('todo');
let input$ = Rx.Observable.fromEvent(todo, 'keyup').pluck('target', 'value');

let addBtn = document.getElementById('addBtn');
let buttonClick$ = Rx.Observable
                      .fromEvent(addBtn, 'click')
                      .mapTo('clicked');

Rx.Observable.combineLatest(buttonClick$, input$, (ev, input)=>{
  return {
    ev: ev,
    input: input
  }
}).subscribe(value => console.log(value))

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第9张图片
Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第10张图片

  • debounceTime(500)延迟500毫秒
  • distinctUntilChanged()输入值不变不发请求
  • switchMap()保证请求顺序

这些都可以帮助我们来完成请求。

36、RxJs在angular中的运用——基本用法 vs 管道用法

基本用法 :

import { Component, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs’; 
import 'rxjs/add/observable/interval';

@Component({
  selector: 'app-playground',
  templateUrl: './playground.component.html',
  styleUrls: ['./playground.component.css']
})
export class PlaygroundComponent implements OnDestroy{
  clock: number;
  subscription: Subscription;

  constructor() { 
    this.subscription = Observable.interval(1000)

      .do(_ => console.log('observable created'))
      .subscribe(value => this.clock= value);
  }

  ngOnDestroy(){
    if(this.subscription !== undefined)
      this.subscription.unsubscribe();
  }
}

{{clock}}

:这段代码还可以有另一种写法

  • 第13行增加:private alive: boolean = true;
  • 第16行增加:.takeWhile(() => this.alive)
  • 第23行更改为:this.alive = false;

管道用法:

import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';

@Component({
  selector: 'app-objdemo',
  templateUrl: './objdemo.component.html',
  styleUrls: ['./objdemo.component.scss']
})
export class ObjdemoComponent {
  clock = Observable.interval(1000).do(_ => console.log('observable created'));

  constructor() { }
}

{{clock | async}}

37、RxJs在angular中的运用——Subject 应用

message.service.ts

import { Injectable } from '@angular/core';
import {Observable, Subject} from 'rxjs';

@Injectable()
export class MessageService {
    private subject = new Subject();

    sendMessage(message: string) {
        this.subject.next({ text: message });
    }

    clearMessage() {
        this.subject.next();
    }

    getMessage(): Observable {
        return this.subject.asObservable();
    }
}

asObservable 函数是为了把一个 Observable 对象包装起来并安全的分享给其他人使用。
message.component.ts

import { Component } from '@angular/core';
import { Subscription } from 'rxjs';
import { MessageService } from '../ message / message.service';
@Component({
  moduleId: module.id,
  templateUrl: 'objdemo.component.html'
})
export class ObjdemoComponent {
  message: any;
  subscription: Subscription;
  constructor(private messageService: MessageService) {
    this.subscription = this.messageService.getMessage()
      .subscribe(message => { 
            this.message = message ? message.text : ‘’;
   });
  }
  sendMessage(): void {    
     this.messageService.sendMessage(‘Message!');
  }
  clearMessage(): void { // 清除消息
    this.messageService.clearMessage();
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

message.component.html

{{message}}

Rxjs入门4-Scheduler[调度器类型、调度器的使用]、RxJs实例[变量赋值、体质指数(BMI)、事件的运用、angular中的运用]、相关思考问题、参考资料_第11张图片

38、RxJs在angular中的运用——http服务

members.service.ts

import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
import { Observable, ObservableInput } from "rxjs/Observable";

@Injectable()
export class CommonService {
    private headers = new Headers({ 'Content-Type': 'application/json' });

    constructor(private http: Http) { }

    get(url): Observable {
        return this.http.get(url)
            .map(response => response.json())
            .catch(this.handleError);
    }}

    public handleError(error:any): ObservableInput {
        return Observable.throw(error);
    }
}

members.commonet.ts

import { Component } from '@angular/core';
import { MembersService } from '../../members.service';
import { AppSettings } from "../../app.settings"; 

@Component({
  selector: 'app-members’,
  templateUrl: './members.component.html',
  styleUrls: ['./members.component.scss']
})
export class MembersComponent{
  private membersUrl = `${AppSettings.env_vars.API_URL}/members`;
  rows = [];

 constructor(private membersService MembersService ) {
       this. getMembers();
  } }

  getMembers(): void {
    this. membersService.get(this.membersUrl)
    .subscribe(members => {
           this.rows = members;
     } , error => { console.log(error) }, () => { });
  }

39、RxJs相关思考问题

  • 如何使用 RxJS 处理多个 Http 请求?多个请求包括两种情况:①当我们发送下一个请求,需要依赖于上一个请求的数据时(mergeMap);②出现多个并行的 Http 请求时(forkJoin)。(使用 RxJS 处理多个 Http 请求)
  • Rxjs结合的观察者模式、迭代器模式 和 使用集合的函数式编程(后两种Operator)的具体实现都有哪些?( Observable详解、 RxJS Functional Programming)
  • 操作符能不能自定义? Observable呢?( RxJS - Observables, observers 和 operators 简介)
  • Angular中其他用到Rxjs的场景?
  • 前端有了 observable,是不是基本可以淘汰 promise 了?(toPromise())
  • 很多网友称Rxjs知识太抽象,学习曲线高,你觉得呢?

40、RxJs参考资料

  • http://reactivex.io/rxjs/ (官方文档-英文版)
  • http://cn.rx.js.org (RxJs中文网)
  • http://xgrommx.github.io/rx-book/content/observable/observable_instance_methods/toarray.html (rxjs教程)
  • https://www.gitbook.com/book/mcxiaoke/rxdocs/details ( ReactiveX文档中文翻译)
  • http://rxmarbles.com/ (常用rxjs方法的交互图)
  • https://github.com/ReactiveX/rxjs (rxjs源码)
  • https://github.com/RxJS-CN/RxJS-Docs-CN (RxJS 5 中文文档)
  • https://github.com/xufei/blog/issues/38 (流动的数据——使用 RxJS 构造复杂单页应用的数据逻辑)
  • https://zhuanlan.zhihu.com/p/25552305(知乎 寸志-可能是最好的 Rx 初学者教程)
  • https://zhuanlan.zhihu.com/p/23331432(知乎 太狼- Hello RxJS)
  • https://zhuanlan.zhihu.com/p/23464709(知乎 太狼-用 RxJS 连接世界)
  • https://www.jianshu.com/p/441d5442250c (简书-看动画,学 RxJS)
  • https://www.jianshu.com/p/eaf877d1274f (简书-如何理解Rxjs)
  • https://www.jianshu.com/p/869a3f74d3ca (简书- Rx–隐藏在Angular 2.x中利剑)
  • https://segmentfault.com/a/1190000008464065 (segmentfault-通俗的方式理解Rx.js)
  • https://segmentfault.com/a/1190000008886598 (segmentfault- RxJS - Subject)
  • https://segmentfault.com/a/1190000010088631 (segmentfault-使用 RxJS 处理多个 Http 请求)

:文章先总结到这,有遗漏的要点欢迎补充!

相关链接

1、程序员分类目录导航
2、Rxjs入门1
3、Rxjs入门2
4、Rxjs入门3

你可能感兴趣的:(JavaScript)