惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Latest news
Latest news
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
博客园 - 司徒正美
B
Blog RSS Feed
AWS News Blog
AWS News Blog
MyScale Blog
MyScale Blog
Scott Helme
Scott Helme
Cisco Talos Blog
Cisco Talos Blog
Last Week in AI
Last Week in AI
NISL@THU
NISL@THU
博客园 - Franky
P
Proofpoint News Feed
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
S
Schneier on Security
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
G
GRAHAM CLULEY
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
WordPress大学
WordPress大学
The Hacker News
The Hacker News
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Microsoft Azure Blog
Microsoft Azure Blog
T
The Exploit Database - CXSecurity.com
Engineering at Meta
Engineering at Meta
罗磊的独立博客
T
The Blog of Author Tim Ferriss
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
SecWiki News
SecWiki News
云风的 BLOG
云风的 BLOG
美团技术团队
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Security Latest
Security Latest
C
Cyber Attacks, Cyber Crime and Cyber Security
B
Blog
S
Security Affairs

博客园 - 皮皮虾的blog

visual studio 2022 安装net旧版本(net framework4.0和4.5) RxJS中高阶映射操作符的全面讲解:switchMap, mergeMap, concatMap (and exhaustMap) 从0到1: Angular + .NET Core 前后端分离项目Compass的开发过程 rxjs学习 - 2 Asynchronous programming with async and await Angular 表单介绍 TypeScript null undefine判断 TypeScript 常见方法 VMware虚拟机如何设置CentOS 7 磁盘扩容? MySQL 5.7 配置 MySQL 主从复制相关 Mysql 常用命令 MySQL 5.7 Sleep 连接太多的处理方式 MySQL 5.7 CPU高的定位方法 VS2019 设置tab插入空格不好使的解决方法 CentOS 7 MySQL 5.7 主从设置 VMware安装CentOS7后配置静态IP MySQL explain,type分析(转) - 2 MySQL explain,Extra分析(转) - 1
rxjs学习
皮皮虾的blog · 2023-03-17 · via 博客园 - 皮皮虾的blog
基本概念
Observable (可观察对象):

表示一个概念,这个概念是一个可调用的未来值或事件的集合。相当于Promise对象,内部可以用于执行异步代码,通过调用内部提供的方法将异步代码执行的结果传递到可观察对象外部。

Observer (观察者):

一个回调函数的集合,它知道如何去监听由 Observable 提供的值。类比then, 方法中的回调函数,用于接收可观察对象中传递出来的数据

Subscription (订阅):

表示 Observable 的执行,主要用于取消 Observable 的执行。类比then方法.

Operators (操作符):

采用函数式编程风格的纯函数 (pure function),使用像 map、filter、concat、flatMap 等这样的操作符来处理集合。

Subject (主体):

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

Schedulers (调度器):

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

讲解一下观察者模式,怕有的人不了解或遗忘了。
观察者模式(observer),又叫发布-订阅模式(publish/subscribe)。这里说下,发布-订阅模式其实有的说他是一种消息范式。并不是一种设计模式。在维基百科中也是有说明的,不过在后来的vue和react中都有使用,可能大家也就默认了吧。但是影响不大。个人举个例子说明一下:
你去超市买烟,但是呢,烟没货了,你就跟老板说:“老板,等烟到货了,记得通知我”。然后烟到货后,老板通知你,你就来买了。在代码中,也是这样,你不知道某个事件何时触发,只能在其触发时去执行相应的逻辑。所以这里面,老板那里是个observable,可观察的对象,而你呢,就是一个observer,观察者。你们这件这种通知的关系,就是一种订阅的关系。

//定义烟店的老板
const smoke_boss = new Observable(todo => {
//等烟到货了,通知买家
setTimeout(() => {
todo.next("烟到货了")
}, 1000);

})
//定义买家1
const buy_one = {
next: function (data:any) {
console.log(data)
}
}
//建立关系。也就是订阅
smoke_boss.subscribe(buy_one)

当Observable产生一个新值时,会通知 observer 的 next(),而当捕获失败可以调用 error()。

当Observable被订阅后,除非调用observer的 complete() 或 unsubscribe() 取消订阅两情况以外;会一直将值传递给 observer。

Observable的生产的值允许经过一序列格式化或操作,最终得到一个有价值的数据给观察者,而这一切是由一序列链式operator来完成的,每一个operator都会产生一个新的Observable。而我们也称这一序列过程为:流。

什么是operator?
正如前面说到的,Observable可以链式写法,这意味着我们可以这样:

Observable.fromEvent(node, 'input')
  .map((event: any) => event.target.value)
  .filter(value => value.length >= 2)
  .subscribe(value => { console.log(value); });

下面是整个顺序步骤:

假设用户输入:a
Observable对触发 oninput 事件作出反应,将值以参数的形式传递给observer的 next()。
map() 根据 event.target.value 的内容返回一个新的 Observable,并调用 next() 传递给下一个observer。
filter() 如果值长度 >=2 的话,则返回一个新的 Observable,并调用 next() 传递给下一个observer。
最后,将结果传递给 subscribe 订阅块。
你只要记住每一次 operator 都会返回一个新的 Observable,不管 operator 有多少个,最终只有最后一个 Observable 会被订阅。

不要忘记取消订阅
为什么需要取消订阅

Observable 当有数据产生时才会推送给订阅者,所以它可能会无限次向订阅者推送数据。正因为如此,在Angular里面创建组件的时候务必要取消订阅操作,以避免内存泄漏,要知道在SPA世界里懂得擦屁股是一件必须的事。

unsubscribe
前面示例讲过,调用 subscribe() 后,会返回一个 Subscription 可用于取消操作 unsubscribe()。最合理的方式在 ngOnDestroy 调用它。

ngOnDestroy() {
    this.inputSubscription.unsubscribe();
}

takeWhile
如果组件有很多订阅者的话,则需要将这些订阅者存储在数组中,并组件被销毁时再逐个取消订阅。但,我们有更好的办法:

使用 takeWhile()
operator,它会在你传递一个布尔值是调用 next() 还是 complete()。

private alive: boolean = true;
ngOnInit() {
  const node = document.querySelector('input[type=text]');

  this.s = Observable.fromEvent(node, 'input')
    .takeWhile(() => this.alive)
    .map((event: any) => event.target.value)
    .filter(value => value.length >= 2)
    .subscribe(value => { console.log(value) });
}

ngOnDestroy() {
  this.alive = false;
}

简单有效,而且优雅。

Subject
如果说 Observable 与 observer 是攻受结合体的话,那么 Subject 就是一个人即攻亦受。正因为如此,我们在写一个Service用于数据传递时,总是使用 new Subject。

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

    send(message: any) {
        this.subject.next(message);
    }

    get(): Observable<any> {
        return this.subject.asObservable();
    }
}

当F组件需要向M组件传递数据时,我们可以在F组件中使用 send()。

constructor(public srv: MessageService) { }

ngOnInit() {
    this.srv.send('w s k f m?')
}

而M组件只需要订阅内容就行:

constructor(private srv: MessageService) {}

message: any;
ngOnInit() {
    this.srv.get().subscribe((result) => {
        this.message = result;
    })
}

EventEmitter
其实EventEmitter跟RxJS没有直接关系,因为他是Angular的产物,而非RxJS的东西。或者我们压根没必要去谈,因为EventEmitter就是Subject。

EventEmitter的作用是使指令或组件能自定义事件。

@Output() changed = new EventEmitter<string>();

click() {
    this.changed.emit('hi~');
}
@Component({
  template: `<comp (changed)="subscribe($event)"></comp>`
})
export class HomeComponent {
  subscribe(message: string) {
     // 接收:hi~
  }
}

上面示例其实和上一个示例中 MessageService 如出一辙,只不过是将 next() 换成 emit() 仅此而已。

结论
RxJS最难我想就是各种operator的应用了,这需要一些经验的积累。

RxJS很火很大原因我认还是提供了丰富的API,以下是摘抄:

创建数据流:

单值:of, empty, never
多值:from
定时:interval, timer
从事件创建:fromEvent
从Promise创建:fromPromise
自定义创建:create
转换操作:

改变数据形态:map, mapTo, pluck
过滤一些值:filter, skip, first, last, take
时间轴上的操作:delay, timeout, throttle, debounce, audit, bufferTime
累加:reduce, scan
异常处理:throw, catch, retry, finally
条件执行:takeUntil, delayWhen, retryWhen, subscribeOn, ObserveOn
转接:switch
组合数据流:

concat,保持原来的序列顺序连接两个数据流
merge,合并序列
race,预设条件为其中一个数据流完成
forkJoin,预设条件为所有数据流都完成
zip,取各来源数据流最后一个值合并为对象
combineLatest,取各来源数据流最后一个值合并为数组
另外最好使用 $ 结尾的命名方式来表示Observable,例:input$。

angular 使用rxjs 监听同级兄弟组件数据变化

angular 的官网给出了父子组件之间数据交互的方法,如ViewChild、EventEmitter

但是如果要在同级组件之间进行数据同步,似乎并没有给出太多的信息。

有时候我们想,在一个组件中修改数据之后,马上反映到另外一个组件中, 或者可能需要调用另外一个组件中的方法,这时候,我们就可以借助于 rxjs 了。

下面源码在 https://github.com/eleven26/angular-observer

1、首先,我们定义一个用于在应用内进行数据交互的 service

import {Injectable} from '@angular/core';
 
@Injectable()
export class StoreService {
  public storageObj = {};
 
  set(key, value) {
    this.storageObj[key] = value;
  }
 
  get(key) {
    return this.storageObj[key];
  }
 
  remove(key) {
    delete this.storageObj[key]
  }
}

2、我们在需要监听数据变化的组件中加入以下代码,此处是直接写在了 AppComponent 中

import {Component, OnInit} from '@angular/core';
import {StoreService} from './store.service';
import {Observable} from 'rxjs/Observable';
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  title = 'app works!';
 
  constructor(private storeService: StoreService) {}
 
  ngOnInit() {
    let myObserver: Observable<any> = Observable.create((observer) => {
      // payload 用以存放不同组件之间需要交互的数据
      let payload = this.storeService.get('payload');
      if (payload) {
        // do something
        if (payload['type'] == 'set-title') {
          this.title = payload['title'];
        }
 
        this.storeService.remove('payload');
      }
 
      observer.next();
    });
 
    // 保存到公共 service 中,以便不同组件之间可以使用同一个观察者对象
    this.storeService.set('myObserver', myObserver);
  }
}

上面的代码中,我们定义了一个可观察对象 myObserver,我们在此方法上调用 subscribe 的时候,Observable.create 的回调就会被调用,

然后,我们从 StoreSrevice 中取得需要处理的数据,具体数据格式自由规定,

我们处理完数据之后,把相关数据从 StoreSrevice 中移除

同时,我们需要把 myObserver 保存到 StoreService 中,以便不同组件获取到的是同一个 observer。

3、定义一个 A 组件,用来生产 payload,类似于 生产者-消费者

import {Component, OnInit} from '@angular/core';
import {StoreService} from './store.service';
import {Observable} from 'rxjs/Observable';
 
@Component({
  selector: 'a-component',
  template: `
    <h1>A Component'title: {{title}}</h1>
 
    <input type="text" (keyup)="change($event.target.value)">
  `,
  styles: []
})
export class AComponent implements OnInit{
  title = 'a title';
 
  public myObserver: Observable<any>;
 
  constructor(private storeService: StoreService) {}
 
  ngOnInit() {
    this.myObserver = this.storeService.get('myObserver');
  }
 
  change(new_title) {
    this.title = new_title;
    this.storeService.set('payload', {
      type: 'set-title',
      title: new_title
    });
    this.myObserver.subscribe(() => {});
  }
}

我们在该组件输入 title 的时候,发现 AppComponent 的 title 也发生了相应的变化。

这样一来,我们的效果就实现了。
image