Angular进阶之十:toPromise废弃原因及解决方案
背景
Rxjs从V7开始废弃了toPromise, V8中会删除它。
原因
1:toPromise()只返回一个值
toPromise()将 Observable 序列转换为符合 ES2015 标准的 Promise 。它使用 Observable 序列的最后一个值。
例:
import { Observable } from "rxjs"; …… myObservable = new Observable<string>((observer) => { observer.next('Hello'); observer.next('World'); observer.complete(); });
获取并打印结果,toPromise()后只获取了最后一个结果'World'
this.myObservable.toPromise().then((value: string) => { console.log(`toPromise: ${value}`); });
2:Observable 可能不产生值或产生多个值
Observable 和 Promise 之间的相似之处在于,两者都可能随着时间的推移产生值,但不同之处在于,Observable 可能不产生值或产生多个值,而 Promise在成功解析时仅产生一个值。
例:增加代码如下
this.myObservable.subscribe((value: string) => { console.log(`observable: ${value}`); });
Observable产生两个值
如果我们注释掉下面代码,Observable不产生值但Promise产生一个undefined值,
myObservable = new Observable<string>((observer) => { // observer.next('Hello'); // observer.next('World'); observer.complete(); });
因此,在V7 中,Observable 的 toPromise() 方法的返回类型已修复,以更好地反映 Observable 可以产生零值的事实。对于某些项目来说,这可能是一个重大变化,因为返回类型已从 Promise<T> 更改为 Promise<T | undefined>。
3:toPromise() 方法名不明确
toPromise() 方法名称从未指示 Promise 将解析为发出的哪个值,因为 Observable 可以随时间产生多个值。转换为 Promise 时,您可能希望选择要选择哪个值 - 第一个到达的值或最后一个值。为了解决所有这些问题,rxjs决定弃用 toPromise(),
解决方案
作为已弃用的 toPromise() 方法的替代,您应该使用两个内置静态转换函数 firstValueFrom 或 lastValueFrom。
1:lastValueFrom
lastValueFrom 几乎与 toPromise() 完全相同,这意味着它将在 Observable 完成时解析为到达的最后一个值。
例:增加代码
const lastValue = await lastValueFrom(this.myObservable); console.log(`lastValueFrom: ${lastValue}`);
但当 Observable 完成而未发出值时,行为有所不同。当 Observable 完成而未发出时,toPromise() 将成功解析为 undefined(因此返回类型会发生变化),而 lastValueFrom 将拒绝并返回 EmptyError。因此,lastValueFrom 的返回类型是 Promise<T>,就像 RxJS 6 中的 toPromise() 一样。
例:
myObservable = new Observable<string>((observer) => { // observer.next('Hello'); // observer.next('World'); observer.complete(); }); this.myObservable.toPromise().then((value: string | undefined) => { console.log(`toPromise: ${value}`); }); const lastValue = await lastValueFrom(this.myObservable); console.log(`lastValueFrom: ${lastValue}`);
2:firstValueFrom
但是,您可能希望在第一个值到达时立即获取它,而不等待 Observable 完成,因此您可以使用 firstValueFrom。firstValueFrom 将使用 Observable 发出的第一个值解析 Promise,并立即取消订阅以保留资源。
例:增加代码
const firstValue = await firstValueFrom(this.myObservable); console.log(`firstValueFrom: ${ firstValue}`);
如果 Observable 完成且未发出任何值,firstValueFrom 也会拒绝并出现 EmptyError。
例:
myObservable = new Observable<string>((observer) => { // observer.next('Hello'); // observer.next('World'); observer.complete(); }); this.myObservable.toPromise().then((value: string | undefined) => { console.log(`toPromise: ${value}`); }); const firstValue = await firstValueFrom(this.myObservable); console.log(`firstValueFrom: ${ firstValue}`);
默认值
无论是哪种替代方案,我们都可以设置默认值,以防止EmptyError发生。
例:
const firstValue = await firstValueFrom(this.myObservable, { defaultValue: "''" }); console.log(`firstValueFrom: ${ firstValue}`); const lastValue = await lastValueFrom(this.myObservable, { defaultValue: "''" }); console.log(`lastValueFrom: ${lastValue}`);
最后
仅当您知道 Observable 最终会完成时才使用 lastValueFrom 函数。如果您知道 Observable 将发出至少一个值或最终会完成,则应使用 firstValueFrom 函数。
如果源 Observable 未完成或未发出,您最终会得到一个挂起的 Promise,并且异步函数的所有状态都可能挂在内存中。
为了避免这种情况,请考虑添加 timeout、take、takeWhile 或 takeUntil 等。