Rxjs快速食用手册

287 阅读7分钟

概念

什么是rxjs

  • rxjs是一个用来处理异步编程的js库,目标是使得编写异步和基于回调的代码更容易实现

快速入门

名词解读

  • 可观察对象(Observable):类比为promise对象,内部可以用于执行异步代码,通过调用内部提供的方法将异步代码执行的结果传递到可观察对象的外面
  • 观察者(Observer):类比then中的回调函数,用于接受可观察对象中传递出来的数据
  • 订阅(Subscribe):类比then方法,通过订阅将可观察者和观察者联系起来,当可观察者发出数据时,订阅者可以接受到数据

Observer(观察者)---->订阅(Subscribe)----> Observable(可观察者)

Observable(可观察者) ---->传递数据(nect )---->Observer(观察者)


尝试使用

//引入可观察对象(Observable)
import {Observable} from 'rxjs'

//创建对象observable并传递一个函数
const observable = new Observable((observer)=>{
  //执行异步操作
  setTimeout(() => {
      //两秒后获取一个数据并使用参数observer的next方法将数据传递到可观察对象的外部
      observer.next( {name:"张三"})
  }, 2000);
})

//创建观察者对象
const ob = {
  //获取可观察者内部传递出来的数据
  next:(value:any)=>{
  console.log(value,"传递出来的值")//{name: '张三'} '传递出来的值'
  }
}

////ob订阅可观察者observable
observable.subscribe(ob)

可观察对象特性(Observable)

  • 在Observable内部可以多次调用next() 向外部发送数据
    //创建对象observable并传递一个函数
const observable = new Observable((observer)=>{
  //执行异步操作
  setTimeout(() => {
      //两秒后获取一个数据并使用参数observer的next方法将数据传递到可观察对象的外部
      observer.next( {name:"张三"})
    +  observer.next( "多次调用next")
  }, 2000);
  
})

//创建观察者对象
const ob = {
  //获取可观察者内部传递出来的数据
  next:(value:any)=>{
  console.log(value,"传递出来的值")
  }
}

//ob订阅可观察者observable
observable.subscribe(ob)
    
  }

  • 当所有数据发送完成之后,可以使用complete方法终止向外部发送数据
//创建对象observable并传递一个函数
const observable = new Observable((observer)=>{
  //执行异步操作
  setTimeout(() => {
      //两秒后获取一个数据并使用参数observer的next方法将数据传递到可观察对象的外部
      observer.next( {name:"张三"})
      // 在这里终止,下面的 observer.next( "多次调用next")将不再执行
    +  observer.complete()
      observer.next( "多次调用next")
  }, 2000);

  
})

//创建观察者对象
const ob = {
  //获取可观察者内部传递出来的数据
  next:(value:any)=>{
  console.log(value,"传递出来的值")
  }
}

//ob订阅可观察者observable
observable.subscribe(ob)
  • 当可观察者内部发生错误时,可以使用error方法将错误信息发送给订阅者
 //创建对象observable并传递一个函数
const observable = new Observable((observer)=>{
  //执行异步操作
  setTimeout(() => {
      //两秒后获取一个数据并使用参数observer的next方法将数据传递到可观察对象的外部
      observer.next( {name:"张三"})
   
      observer.error("当可观察者内部发生错误时,可以使用error方法将错误信息发送给订阅者")
         // 在这里终止,下面的 observer.next( "多次调用next")将不再执行
      observer.complete()
      observer.next( "多次调用next")
  }, 2000);
})

//创建观察者对象
const ob = {
  //获取可观察者内部传递出来的数据
  next:(value:any)=>{
  console.log(value,"传递出来的值")
  },
  error:(error:any)=>{
    console.log(error,"errorMessage")
  }
}

//ob订阅可观察者observable
observable.subscribe(ob)

  • 可观察者是惰性的,只有被观察者订阅后才会执行
  • 可观察者可以被多个观察者订阅,每次订阅都会被执行

Subject(官网称为:主体)

  • subject可以用来创建可观察对象,但是在订阅后不会立即执行
 //引入主体(subject)
import { Subject } from 'rxjs';
 
 //创建一个空的可观察者
  const sb = new Subject()
  sb.subscribe({next:(value)=>{console.log(value)}})
  sb.subscribe({next:(value)=>{console.log(value)}})
   
  //next()  方法在可观察者外部调用
  //应用场景: Subject可以有多个订阅者,在订阅的时候不会理解执行,当有需求的时候,在subject外部执行next方法,统一给订阅者发送数据
  setTimeout(() => {
    sb.next('哈哈')
  }, 2000);
  }

BehaviorSubject

  • Subject 的一种变体,拥有subject的全部功能,但是可以在创建可观察者的时候,传入默认值,观察者订阅后可以获取默认值
import {BehaviorSubject} from 'rxjs'  
//创建一个空的可观察者
  const sb = new BehaviorSubject("传入默认值")
  sb.subscribe({next:(value)=>{console.log(value)}})
  sb.subscribe({next:(value)=>{console.log(value)}})

  //next()  方法在可观察者外部调用
  //应用场景:这边会先打印2次传入默认值  2秒后  在打印2次哈哈
  setTimeout(() => {
    sb.next('哈哈')
  }, 2000);

ReplaySubject

  • Subject 的一种变体,会广播所有历史记录
import {ReplaySubject} from 'rxjs'   
//创建一个空的可观察者
  const sb = new ReplaySubject()
  sb.subscribe({next:(value)=>{console.log(value)}})
  sb.next('1')
  sb.next('2')

  setTimeout(() => {
    //新的订阅者  
    //先打印 1,2     2 秒后  再次打印 1,2
    sb.subscribe({next:(value)=>{console.log(value)}})
  }, 2000);

操作符

概念介绍

  • 从可观察者对象中发送出的数据,就是数据流

  • 用于操作数据流,可以对对象数据流进行处理,就是操作符

常用辅助方法(都会返回新的可观察对象)

  • range 创建一个可发送指定范围内的数字序列的 Observable。
import {range} from 'rxjs'
range(1,10).subscribe((n)=>{
      console.log(n)
    })

  • scheduled (of 最新版 已弃用 scheduled 代替)
import { of ,scheduled } from 'rxjs';
//of
of(1,2,3).subscribe((val)=>{
   console.log(val)
   })
//scheduled(没整明白呢 暂时没有哈)


  • from 将数组丶promise丶迭代器转换为可观察对象
import { from } from 'rxjs';
//数组
from([1,2,3]).subscribe((val)=>{
   console.log(val)
   })

//promise
upload(){
   from(this.p()).subscribe((val)=>{
   console.log(val)
   })
  }

  p(){
    return new Promise((res,req)=>{
     setTimeout(() => {
      res({name:'我是promise'})
     }, 1000);
    })
  }

  • interval丶timer

    • interval每隔一段时间发送一个数值
    interval(1000).subscribe(console.log)//每个一秒输出   从0开始  0,1,2,3,4,5,6.....
    

    • 间隔时间过去之后发送数值,行为终止,或间隔时间发出数值后,继续按照第二个参数的时间继续发出值
    示例1
    timer(1000,6000).subscribe(console.log) //一秒后发出值   之后每6秒发出一次值
    
    示例2
    upload(){
        //一秒后 发送一次请求   之后每6秒发送一次
        timer(1000,6000).pipe(switchMap(e=>from(this.p()))).subscribe(console.log)
      }
    
      p(){
          //模拟后端请求
        return new Promise((res,req)=>{
         setTimeout(() => {
          res({name:'我是promise'})
         }, 1000);
        })
      }
    

  • forkJoin 是rxjs版本的promise.all 会在所有Observable执行完成之后,才一次性返回值

import { from,forkJoin} from 'rxjs';
upload(){
  forkJoin(
      {
          name1:from(this.p()),
          name2:from(this.p())
      }
  ).subscribe(console.log)
  }

  p(){
    return new Promise((res,req)=>{
     setTimeout(() => {
      res({name:'我是promise'})
     }, 1000);
    })
  }

  • fromEvent 将事件转换为可观察对象
import { fromEvent,map } from 'rxjs';
upload(){
    //这里这个document 可是是任何dom元素 可通过document.getElementById()等方法获取
fromEvent(document, 'click').pipe(map(event=>event.target)).subscribe((val)=>{console.log(val)})
  }

常用操作符

  • map

import {range} from 'rxjs'
//引入操作符
import { map } from 'rxjs';
//使用操作符,让数据流的每一个数据都减去1
range(1,10).pipe(map(i=>i-1)).subscribe((n)=>{
      console.log(n)
    })
  • pluck(弃用) 与 map类似 直接获取指定属性
fromEvent(document, 'click').pipe(pluck('target')).subscribe((val)=>{console.log(val)})

  • switchMap切换可观察对象
//示例1
fromEvent(document,'click').pipe(switchMap(event=> interval(1000))).subscribe(console.log)

//示例二
 upload(){
   fromEvent(document,'click').pipe(switchMap(event=> from(this.p()))).subscribe(console.log)
  }

  p(){
    return new Promise((res,req)=>{
     setTimeout(() => {
      res({name:'我是promise'})
     }, 1000);
    })
  }

  • take 获取数据流中的前几个
//获取数据流中的前3个
 range(1,10).pipe(take(3)).subscribe(console.log)

  • takeWhile 根据条件从数据源前面开始获取(条件获取)
//获取小于3的
  range(1,10).pipe(takeWhile(n=>n<3)).subscribe(console.log)//打印:1,2

  • takeUntil 接收可观察对象,当观察对象发出值时,终止主数据
  const btn = document.getElementById('stop')!
        //当触发点击事件之后   终止主程序
  fromEvent(document,'mousemove').pipe(takeUntil(fromEvent(btn,'click'))).subscribe(console.log)

  • throttleTime 节流
//三秒内只执行一次
const btn = document.getElementById('stop')!
fromEvent(btn,'click').pipe(throttleTime(3000)).subscribe(console.log)

  • debounceTime 防抖 触发高频事件,只响应最后一次
//三秒内只执行最后一次点击
    const btn = document.getElementById('stop')!
    fromEvent(btn,'click').pipe(debounceTime(3000)).subscribe(console.log)

  • distinctUntilChanged 检测当前数据源发出的数据流是否改变 如果没有改变那么就不发出
   
of(1,2,3,3).pipe(distinctUntilChanged()).subscribe(console.log)  //1,2,3

综合案例之搜索框

  • html
<input type="text" placeholder="请输入内容" id="search">
  • ts
import { Component, OnInit } from '@angular/core';

import { fromEvent, takeUntil,of} from 'rxjs';
import { map,pluck,interval,switchMap,from,timer,range,take,takeWhile,throttleTime,debounceTime,distinctUntilChanged} from 'rxjs';

import { HttpClient } from '@angular/common/http';


@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  styleUrls: ['./user-list.component.less']
})
export class UserListComponent implements OnInit{

  constructor(private http:HttpClient){

  }

  // 使用服务
  ngOnInit(): void {
    const search = document.getElementById('search')! 
  fromEvent(search,'keyup').pipe(debounceTime(200),map(e => (e.target as HTMLInputElement).value),distinctUntilChanged(),switchMap(val=>this.http.get(`https://jsonplaceholder.typicode.com/posts?q=${val}`))).subscribe(console.log)
  }
}