Angular Observable 完全指南:核心用法与实战技巧
熟悉 Promise 的朋友可能会觉得 Observable 非常相似——两者确实都能处理异步操作。不过 Observable 在功能上更为强大且灵活。下面我们系统梳理一下 Observable 的典型用法与关键特性。
使用之前,先要在对应组件的文件顶部引入相应模块:
import { Observable } from 'rxjs';
假设我们要创建一个发送异步请求的服务文件 storage.service.ts,把它放在 service 层中,任何需要的地方都可以直接注入使用。
Observable 的基本用法与 Promise 类似:先 new 一个实例,实例接收一个函数参数,函数内部实现异步操作;该函数还接收一个 observer 参数,通过 observer.next 将异步数据推送出来,外部通过订阅即可接收。
// observable 使用之前先引入
getObservable(){
return new Observable((observer)=>{
setTimeout(()=>{
var data = 'observer数据'
observer.next(data) //相当于promise的resolve
},2000)
})
}
然后在 home 组件中调用这个服务。注意:需要先在 home.ts 中引入 storage.service.ts,并在 constructor 里声明注入变量:
import { StorageService } from 'src/app/services/storage.service';
constructor(public storage:StorageService) { }
之后便可在 home 组件中订阅 observer.next 抛出的数据:
let oberverData = this.storage.getObservable()
let d = oberverData.subscribe((res)=>{ //相当于promise的then
console.log(res)
})
然而 Observable 相比 Promise 还提供了几个关键增强能力,下面逐一解析。
1、取消订阅:随时终止数据流
Observable 允许在订阅之后随时手动取消订阅,避免不必要的资源占用或内存泄露。
setTimeout(()=>{
d.unsubscribe() //一秒钟之后取消订阅,接收不到消息
},1000)
2、多次输出:支持持续推送数据
Promise 的状态一旦从 pending 变为 reject 或 resolve 后就不可再变更,因此无法多次输出同一个 resolve 的值。但 Observable 天然支持多次异步推送。请看对比示例:
let p = new Promise(resolve=>{
setInterval(()=>{
resolve('promise interval值')
},2000)
})
p.then(res=>console.log(res)) //只输出一次
// observable
let o = new Observable(observer=>{
setInterval(()=>{
observer.next('observable interval值')
},2000)
})
o.subscribe(res=>console.log(res)) //每隔2秒输出一次
3、使用 pipe 对数据进行变换与过滤
通过 RxJS 的 pipe 操作符,可以对发射的数据进行过滤、映射、组合等复杂处理,实现响应式数据流操作。
let o1 = this.storage.getObservable1()
o1.pipe(
filter((data:any)=> {
return data%2 == 0
}),
map(value => {
return value*value
})
).subscribe(res=>console.log(res))
Angular Observable 数据类型的单元测试 mock 数据准备
假设有一个组件,它的 items 属性是一个嵌套的 Observable:

items$: Observable[]> = this.componentData$.pipe( map((data) => data.productCodes.trim().split(' ')), map((codes) => codes.map((code) => this.productService.get(code, this.PRODUCT_SCOPE)) ) );
ComponentData$ 的类型定义:
private componentData$: Observable= this.componentData.data$.pipe( filter(Boolean) );
Model 接口的定义:

componentData$ 的类型为 Observable,在 map 的回调中又嵌套了另一个 map 操作,因此最终返回的类型是嵌套的 Observable。

由于 items$ 的数据来自 componentData$,而 componentData$ 又源自 componentData,所以只需要构造好 componentData 的模拟数据即可:
private componentData$: Observable= this.componentData.data$.pipe( filter(Boolean) );
下面展示如何在单元测试中创建 mock 数据,示例为 MockCmsProductCarouselComponent:

完整解决方案
const mockComponentData: CmsProductCarouselComponent = {
uid: '001',
typeCode: 'ProductCarouselComponent',
modifiedTime: new Date('2017-12-21T18:15:15+0000'),
popup: 'false',
productCodes: productCodeArray.join(' '),
scroll: 'ALLVISIBLE',
title: 'Mock Title',
name: 'Mock Product Carousel',
container: 'false',
};
const MockCmsProductCarouselComponent = >{
data$: of(mockComponentData),
}; 