{"id":22903417,"url":"https://github.com/sawyerbutton/subscribing-to-multiple-observables-in-angular","last_synced_at":"2025-04-01T07:23:04.139Z","repository":{"id":120750770,"uuid":"151366189","full_name":"sawyerbutton/Subscribing-to-multiple-Observables-in-Angular","owner":"sawyerbutton","description":"Observable multiple subscribe","archived":false,"fork":false,"pushed_at":"2018-10-08T12:50:16.000Z","size":91,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-07T04:40:00.111Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sawyerbutton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-03T05:41:52.000Z","updated_at":"2018-10-08T12:50:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"434a10c5-6004-4358-854f-d01847607952","html_url":"https://github.com/sawyerbutton/Subscribing-to-multiple-Observables-in-Angular","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2FSubscribing-to-multiple-Observables-in-Angular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2FSubscribing-to-multiple-Observables-in-Angular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2FSubscribing-to-multiple-Observables-in-Angular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sawyerbutton%2FSubscribing-to-multiple-Observables-in-Angular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sawyerbutton","download_url":"https://codeload.github.com/sawyerbutton/Subscribing-to-multiple-Observables-in-Angular/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246598804,"owners_count":20803091,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-14T02:36:34.134Z","updated_at":"2025-04-01T07:23:04.132Z","avatar_url":"https://github.com/sawyerbutton.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SubscribeObservablesInAngular\n\n## 如何订阅Angular组件中的多个Observable\n\n\u003e Angular应用程序高度依赖于`RXJS`的`Observable`, 在使用这些技术构建大型前端应用程序时, 需要学习如何在组件中管理订阅多个`Observable`\n\n\u003e 本文将会介绍五种不同的方式用以在Angular组件中订阅多个`Observables`以及其优点和缺点\n\n\u003e 在举例的组件中,我们拥有三种不同的`Observables`,每个`Observables`都拥有不同的表现\n\n1. 第一个Observable将会立刻抛出一个单个的值\n2. 第二个Observable将会延迟一会抛出一个单个的值\n3. 第三个Observable将会每秒抛出一个值\n\n\u003e 下面的服务包含了将在组件中使用的返回上述`Observable`的函数\n\n```typescript\nimport { Injectable } from '@angular/core';\nimport {of} from 'rxjs/index';\nimport {delay} from 'rxjs/operators';\nimport {Observable} from 'rxjs/internal/Observable';\n\n@Injectable({\n  providedIn: 'root'\n})\n// the util service has been injected into root, so every component which injected this service will share it together\nexport class UtilService {\n\n  constructor() { }\n  public getSingleValueObservable() {\n    return of('single value');\n  }\n  public getDelayedValueObservable() {\n    return of('delayed value').pipe(\n      delay(1000)\n    );\n  }\n  public getMultipleValueObservables() {\n    return new Observable\u003cnumber\u003e(observer =\u003e {\n      let count = 0;\n      const interval = setInterval(() =\u003e {\n        observer.next(count++);\n        console.log(`interval has been fired, count ${count}`);\n      }, 1000);\n      return () =\u003e {\n        clearInterval(interval);\n      };\n    });\n  }\n}\n```\n\n\u003e 由于服务注册于root,故而该服务是全局单例的\n\n\u003e 容易引起误解的地方在于全局单例是否意味着在不同的组件中订阅服务中传递的Observable值是一致的\n\n\u003e 通过打印每个组件中的subscribe的Observable值可以明确得知,服务本身是全局单例的与Observable的subscribe是无关的\n\n\u003e 事实上对于每个Observable的subscribe而言,它们是是独立的,subscribe之间不会互相影响,每个组件中的subscribe相当于是独立的对于Observable流的订阅\n\n### 直接手动订阅的方式\n\n```typescript\nimport {Component, OnDestroy, OnInit} from '@angular/core';\nimport {UtilService} from '../util.service';\nimport {Subscription} from 'rxjs/internal/Subscription';\nimport {getDelayedValueObservable, getSingleValueObservable, getMultipleValueObservables} from '../util';\n\n@Component({\n  selector: 'app-manual-subscribe',\n  templateUrl: './manual-subscribe.component.html',\n  styleUrls: ['./manual-subscribe.component.css']\n})\nexport class ManualSubscribeComponent implements OnInit, OnDestroy{\n  show = false;\n  first: string;\n  second: string;\n  third: number;\n  thirdSubscription: Subscription;\n  constructor(\n    private utilService: UtilService\n  ) { }\n\n  ngOnInit() {\n    this.utilService.getSingleValueObservable().subscribe( data =\u003e {\n      this.first = data;\n    });\n    this.utilService.getDelayedValueObservable().subscribe( data =\u003e {\n      this.second = data;\n    });\n    this.thirdSubscription = this.utilService.getMultipleValueObservables().subscribe( data =\u003e {\n      this.third = data;\n    });\n    // not use dependency injection\n  //   getSingleValueObservable()\n  //     .subscribe(value =\u003e this.first = value);\n  //\n  //   getDelayedValueObservable()\n  //     .subscribe(value =\u003e this.second = value);\n  //\n  //   this.thirdSubscription = getMultipleValueObservables()\n  //     .subscribe(value =\u003e this.third = value);\n  }\n  ngOnDestroy() {\n    // if your observable has not been unsubscribe after destroy, the internal operation is still in triggering\n    // this.utilService.getMultipleValueObservables()\n    this.thirdSubscription.unsubscribe();\n  }\n}\n```\n\n\u003e 在这个组件中只需调用`.subscribe()`来获取Observables中的事件, 对于首次使用Angular和RxJS的用户, 直接订阅Observable是开始的地方\n\n\u003e 优点: 手动订阅的方式简单易懂适用于单个值得情况\n\n\u003e 缺点: 如果Observable有多个值,我们必须手动订阅`ngOnDestroy`生命周期钩子, 并在其中取消对于多个值Observables的订阅\n\u003e 如果在组件被销毁时没有取消订阅d额Observable将会存在内存泄漏的问题\n\n### 使用异步管道的方式\n\n\u003e Angular可以使用异步管道功能用以简化之前组件的某些冗余\n\u003e Async管道帮助组件知晓哪些属性是Observables,因此它可以为了组件自动订阅和取消订阅这些属性\n\n```typescript\nimport { Component, OnInit } from '@angular/core';\nimport {UtilService} from '../util.service';\nimport {getDelayedValueObservable, getSingleValueObservable, getMultipleValueObservables} from '../util';\n@Component({\n  selector: 'app-async-pipe',\n  templateUrl: './async-pipe.component.html',\n  styleUrls: ['./async-pipe.component.css']\n})\nexport class AsyncPipeComponent implements OnInit {\n  show = false;\n  first$: any;\n  second$: any;\n  third$: any;\n  constructor(\n    private utilService: UtilService\n  ) { }\n  // async pipe automatically subscribe and unsubscribe to the observables,\n  // starting to subscribe when init the component, ending with unsubscribe when destroy the component\n  ngOnInit() {\n    this.first$ = this.utilService.getSingleValueObservable();\n    this.second$ = this.utilService.getDelayedValueObservable();\n    this.third$ = this.utilService.getMultipleValueObservables();\n    // this.first$ = getSingleValueObservable();\n    // this.second$ = getDelayedValueObservable();\n    // this.third$ = getMultipleValueObservables();\n  }\n}\n```\n\n\u003e 在组建中我们直接把函数的结果直接分配给组件的属性\n\u003e 在使用Observables的时候,我们规约以$为变量属性的结尾用以区分Observable属性和常见的主要属性\n\n```html\n\u003ch2\u003eAsync Pipe\u003c/h2\u003e\n\n\u003cbutton (click)=\"show = !show\"\u003eToggle\u003c/button\u003e\n\n\u003cng-container *ngIf=\"show\"\u003e\n  \u003cp\u003e{{first$ | async}}\u003c/p\u003e\n  \u003cp\u003e{{second$ | async}}\u003c/p\u003e\n  \u003cp\u003emulti values {{third$ | async}}\u003c/p\u003e\n\u003c/ng-container\u003e\n```\n\n\u003e 在我们的模板中, Async管道自动订阅和处理组件中的Observables\n\n\u003e 优点: 将处理Observable的繁重工作交给angular来完成\n\n\u003e 缺点: 当有多个Observables订阅时,这种语法会变得有点复杂,但是这并不是大问题\n\n### 使用ForkJoin和*ngIf的方式\n\n\u003e 有时我们需要同时订阅多个Observable, 典型情况是使用多个HTTP请求并期待同时处理响应时\n\n```typescript\nimport { Component, OnInit } from '@angular/core';\nimport {UtilService} from '../util.service';\nimport {forkJoin} from 'rxjs';\nimport {map} from 'rxjs/operators';\n// this component does not work correctly\n@Component({\n  selector: 'app-fork-join',\n  templateUrl: './fork-join.component.html',\n  styleUrls: ['./fork-join.component.css']\n})\nexport class ForkJoinComponent implements OnInit {\n  show = false;\n  values$: any;\n  constructor(\n    private utilService: UtilService\n  ) { }\n\n  ngOnInit() {\n    this.values$ = forkJoin(\n      this.utilService.getSingleValueObservable(),\n      this.utilService.getDelayedValueObservable()\n      // this.utilService.getMultipleValueObservables(),\n    ).pipe(\n      map(([first, second]) =\u003e {\n        return { first, second};\n      })\n    );\n  }\n}\n```\n\u003e 在组件中使用`forkJoin`将把多个Observable组合成一个值Observable\n\n\u003e `forkJoin`运算符将订阅传递给它的每个Observable, 并在从所有Observable接收到值时将使用每个Observable的值进行组合发出一个新值\n\n\u003e `forkJoin`适用于Angular HttpClient等单值Observable\n\n\u003e `forkJoin`可以与`Promise.All()`类比, 区别在于一个接收Promise一个接收Observable\n\n```html\n\u003ch2\u003eforkJoin Operator\u003c/h2\u003e\n\n\u003cbutton (click)=\"show = !show\"\u003eToggle\u003c/button\u003e\n\n\u003cng-container *ngIf=\"show\"\u003e\n  \u003cng-container *ngIf=\"values$ | async; let values;\"\u003e\n    \u003cp\u003e{{values.first}}\u003c/p\u003e\n    \u003cp\u003e{{values.second}}\u003c/p\u003e\n    \u003cp\u003e{{values.third}}\u003c/p\u003e\n  \u003c/ng-container\u003e\n\u003c/ng-container\u003e\n```\n\n\u003e 在模板中将利用一些Angular模板功能来处理Observables\n\n\u003e ng-container允许使用像`*ngIf`这样的Angular指令,而不需要过多使用div来装在结构性指令\n\n\u003e 通过使用`*ngIf`指令,允许异步管道进行订阅,然后将Observable中的值分配给我们可以在模板中重用的临时模板变量,这样就不必重复使用异步管道了\n\n\u003e 使用`forkJoin`时,直到所有Observable都传回值之前是不会返回值的\n\n\u003e 在演示中延迟的Observable发出其值之前,我们在模板中看不到任何值, \n\n\u003e `forkJoin`可以帮助处理多个传递单值得Observable, 但是在处理传递多个值得Observable时`forkJoin`就不那么好用了\n\n### 使用combineLatest的方式\n\n\u003e `combineLatest`就像是`forkJoin`的高级版本,`combineLatest`可以处理接受多个值得Observables\n\n```typescript\nimport { Component, OnInit } from '@angular/core';\nimport {UtilService} from '../util.service';\nimport { map} from 'rxjs/operators';\nimport { combineLatest} from 'rxjs';\n\n// this component does not work correctly\n@Component({\n  selector: 'app-combine-latest',\n  templateUrl: './combine-latest.component.html',\n  styleUrls: ['./combine-latest.component.css']\n})\nexport class CombineLatestComponent implements OnInit {\n  show = false;\n  values$: any;\n  constructor(\n    private utilService: UtilService\n  ) { }\n\n  ngOnInit() {\n    this.values$ = combineLatest(\n      this.utilService.getSingleValueObservable(),\n      this.utilService.getDelayedValueObservable(),\n      this.utilService.getMultipleValueObservables()\n    ).pipe(\n      map(([first, second, third]) =\u003e {\n        // combineLatest returns an array of values, here we map those values to an object\n        return { first, second, third };\n      })\n    );\n  }\n}\n```\n\n\u003e 当`combineLatest`接受每个Observables发出的值时将会将会抛出一个混合的值\n\n\u003e 并将在每个Observables发出新的值时持续抛出混合值\n\n\u003e 简单来说就是新来的混作一团一起发出\n\n```html\n\u003ch2\u003ecombineLatest Operator\u003c/h2\u003e\n\n\u003cbutton (click)=\"show = !show\"\u003eToggle\u003c/button\u003e\n\n\u003cng-container *ngIf=\"show\"\u003e\n  \u003cng-container *ngIf=\"values$ | async; let values;\"\u003e\n    \u003cp\u003e{{values.first}}\u003c/p\u003e\n    \u003cp\u003e{{values.second}}\u003c/p\u003e\n    \u003cp\u003e{{values.third}}\u003c/p\u003e\n  \u003c/ng-container\u003e\n\u003c/ng-container\u003e\n```\n\n\u003e `*ngIf`在模板中的应用和之前没有区别, 但是遇到多个Observables时并不是一定需要使用`forkJoin`或者`combineLatest`\n\n### 订阅Async管道的方式\n\n\u003e 我们也可以使用Async管道在模板中订阅多个Observables, 而不是在组件中订阅\n\n```typescript\nimport { Component, OnInit } from '@angular/core';\nimport {UtilService} from '../util.service';\n\n@Component({\n  selector: 'app-async-pipe-objects',\n  templateUrl: './async-pipe-objects.component.html',\n  styleUrls: ['./async-pipe-objects.component.css']\n})\nexport class AsyncPipeObjectsComponent implements OnInit {\n  first$: any;\n  second$: any;\n  third$: any;\n  show = false;\n  constructor(\n    private utilService: UtilService\n  ) { }\n\n  ngOnInit() {\n    this.first$ = this.utilService.getSingleValueObservable();\n    this.second$ = this.utilService.getDelayedValueObservable();\n    this.third$ = this.utilService.getMultipleValueObservables();\n  }\n\n}\n```\n\n\u003e 组件与之前的示例类似,而模板则是事情变得有趣的地方\n\n```html\n\u003ch2\u003eAsync Pipe Object\u003c/h2\u003e\n\u003cbutton (click)=\"show = !show\"\u003eToggle\u003c/button\u003e\n\u003cng-container *ngIf=\"show\"\u003e\n  \u003cng-container *ngIf=\"{first: first$ | async, second: second$ |async, third: third$ | async} as values;\"\u003e\n    \u003cp\u003e{{values.first}}\u003c/p\u003e\n    \u003cp\u003e{{values.second}}\u003c/p\u003e\n    \u003cp\u003emulti values {{values.third}}\u003c/p\u003e\n  \u003c/ng-container\u003e\n\u003c/ng-container\u003e\n```\n\n\u003e 使用`*ngIf`和异步管道可以将每个Observable分解为一个包含每个Observable的展开值的值对象\n\n\u003e `*ngIf`的这种使用方式并不常见,但这种方式允许在模板中订阅多个Observable以及多次引用之\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsawyerbutton%2Fsubscribing-to-multiple-observables-in-angular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsawyerbutton%2Fsubscribing-to-multiple-observables-in-angular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsawyerbutton%2Fsubscribing-to-multiple-observables-in-angular/lists"}