简介
事件处理是构建TypeScript应用程序时常见的实现之一。
注册一个事件以及在事件被触发时执行一些动作需要一个Observable模式,它为事件流提供了一个单向的流,使事件流的调试和错误处理变得更加容易。
在这篇文章中,我们将探讨Observables,如何用它来处理事件驱动的数据,以及错误和异步数据。
-
- 可观察变量简介
- 可观察的操作符
- 用Observable转换数组
- 结合多个运算符进行事件流转换
- 用Observables处理错误
可观察变量简介
一般来说,JavaScript中有趣的,但有时令人费脑筋的部分是处理事件。
考虑一下像Facebook这样的实时聊天应用程序,它在一个特定的时间范围内有多个事件发生。一个用户可能正在他的新闻源上输入一些文字,同时收到其他朋友的一些通知和消息,而且没有特定的顺序。现在,处理这些事件是应用程序的责任。这就是Observables的作用。
一个Observable是多个输入值的集合,使用数组方法进行处理,如map,reduce,filter, 等等。它在处理异步操作时非常方便,比如提出HTTP请求、用户输入事件等。
可观察模式是一种设计模式,用于注册一个事件,以及在事件被触发时实现一个流程。专门处理事件的最强大、最流行的JavaScript库之一是Reactive Extensions for JavaScript库,也被称为RxJS。
为了让我们开始工作,我们需要确保我们安装了RxJS库。所以,我们用这个命令安装这个库。
npm install rxjs
已经,这个库有TypeScript需要的所有声明文件,所以不需要独立安装。
为了创建一个Observable,我们需要RxJS的Observable 类型和of 方法,如下所示。
import { of, Observable } from "rxjs";
const emitter : Observable<string> = of("Sam", "Ray", "Thomas");
在上面的片段中,我们从RxJS库中导入了of 函数和Observable 类型,然后从字符串"Sam","Ray", 和"Thomas" 中创建了一个Observable 。
接下来,我们将订阅一个Observable,如下所示。
emitter.subscribe((value: string) => {
console.log(`Name: ${value}`)
})
在上面的片段中,我们通过调用emitter 变量上的subscribe 方法来注册一个Observer。由于emitter 变量是Observable类型的,我们可以自动访问subscribe 方法。subscribe 方法接收一个函数作为参数,这个函数将为Observable发出的每个值调用一次。
上面的代码将输出以下的Observable流。
Name: Sam
Name: Ray
Name: Thomas
可观察的操作符
Pipeable Operators和Creation Operators是RxJS中的两种操作符。
Pipeable Operators是将一个Observable作为输入并返回另一个Observable的方法。它们可以使用语法observableInstance.pipe(operator()) ,被输送到Observables。它包括 [filter(...)](https://rxjs.dev/api/operators/filter)和 [mergeMap(...)](https://rxjs.dev/api/operators/mergeMap)方法。
Creation Operators是在调用时创建一个新的Observable的操作符。
创建操作符包括以下内容。
[from](https://rxjs.dev/api/index/function/from)[interval](https://rxjs.dev/api/index/function/interval)[of](https://rxjs.dev/api/index/function/of)[range](https://rxjs.dev/api/index/function/range)[throwError](https://rxjs.dev/api/index/function/throwError)
你可以参考RxJS的官方文档以获得完整的操作符列表。
用Observable转换数组
RxJSfrom 方法允许对数组中的数据进行转换。它接收一个数组作为输入,并将数组中的每个数据转换为一个Observable。
让我们来看看下面的代码片断。
const transformArray: Observable<number> = from([1, 2, 3, 4]);
transformArray.subscribe((value: number) => {
console.log(`value: ${value}`);
});
上面的代码片段使用from 方法将数组中的值转换为Observable,然后在transformArray 变量上调用subscribe 方法。subscribe 方法接受一个函数,该函数对Observable发出的每个值都被执行。
结合多个运算符进行事件流转换
RxJS为我们提供了pipe 方法,该方法允许将多个操作符方法结合起来进行事件流的复杂转换。
让我们考虑一下下面的代码。
const emitter = of(4, 9, 16, 25)
const mapValue = emitter.pipe(
map((value: number) => {
return Math.sqrt(value)
}),
map((value: number) => {
return `square root: ${value}`
})
)
mapValue.subscribe((value: string) => {
console.log(`string value emitted ${value}`)
})
这里,RxJS的of 方法被用来从数字4,9,16, 和25 创建一个Observable。pipe 方法接收了两个map 方法。第一个map 方法返回输入值的平方根,第二个map 方法将第一个map 方法的输出转换为一个字符串。
最后,subscribe 方法在每个发射的Observable上被调用。
运行上述代码将输出如下。
string value emitted square root: 2
string value emitted square root: 3
string value emitted square root: 4
string value emitted square root: 5
观察到的错误
处理Observable流中的异常需要一个结构良好的机制来捕捉这些异常。
让我们考虑一下下面的代码片断。
interface IName {
value: string;
}
interface IObj {
name?: IName;
}
const emitObj: Observable<IObj> = of(
{ name: { value: "Bob" } },
{},
{ name: { value: "Sam" } }
);
在上面的代码片断中,我们创建了IName ,其属性为value ,类型为string 。同时,我们创建了IObj ,并带有一个可选的属性name ,类型为IName 。然后我们创建了emitObj 观察者,它发射了三个值。
现在让我们考虑一下下面这个Observable流。
const returnName = emitObj.pipe(
map((value: IObj) => {
return value.name!.value;
})
);
returnName.subscribe((value: string) => {
console.log(`name: ${value} `)
});
在上面的片段中,我们创建了一个Observable streamreturnName ,它为输入流值返回name.value 属性。然后,我们订阅这个流并将收到的值记录到控制台。
当我们运行这个片段时,我们将得到以下输出。
name: Bob
TypeError: Cannot read property 'value' of undefined
这个错误的发生是因为在我们的Observable流中发出的第二个值没有一个名字属性,导致了一个未定义的值。
为了解决这个错误,我们可以为我们的Observable流中的subscribe 函数创建一个错误处理器。
让我们考虑下面的情况。
returnName.subscribe(
// called for each observable value
(value: string| null) => {
console.log(`name: ${value} `);
},
// called if an error occurs
(error: unknown) => {
console.log(`error : ${error}`);
},
// called when execution is done
() => {
console.log(`done`);
}
);
在这里,我们为subscribe 方法提供了三个函数作为参数,每个由Observable流发出的值都会调用第一个函数,如果发生错误则调用第二个函数,最后一个函数在subscribe 方法执行完成后调用。
当我们运行这个片段时,我们将得到以下输出。
name: Bob
TypeError: Cannot read property 'value' of undefined
这里发生的情况是,Observable流发出的第一个值由提供给subscribe 方法的第一个函数处理。由Observable流发出的第二个值导致了一个错误,这个错误被提供给subscribe 方法的错误函数捕获。由于Observable流中发生的错误,最后一个函数没有被调用。
catchError
另一种处理Observable错误的方法是在Observable流本身中使用catchError 操作符,这样我们就可以足够早地捕获这些错误。
让我们考虑一下下面的代码片断。
const returnName = emitObj.pipe(
map((value: IObj) => {
return value!.name!.value;
}),
catchError((error: unknown) => {
console.log(`stream caught : ${error}`);
return of(null);
})
);
在这里,我们在Observable流中加入了一个catchError 操作符。在这个函数中,我们将错误信息记录到控制台,然后返回一个Observable值:null 。通过这个实现,即使Observable中发生错误,最后一个函数也会被触发。
当我们运行这个片段时,我们将得到以下输出。
Name: Bob
stream caught : TypeError: Cannot read property 'value' of undefined
received null
done
这里发生的情况是,map 方法对Observable流的第二个值产生了错误,之后我们的catchError 函数被调用,出现了以下错误:"不能读取未定义的属性'值'。" 同时,最后一个方法被触发。
一旦Observable流内发生错误,该流将停止发射值。这就是为什么emitObj Observable流不发射其最后一个参数的值。
总结
在这篇文章中,我们已经探讨了用Observable流进行数据转换以及如何实现它们。此外,我们还讨论了Observable的错误处理,并提供了在使用Observable流时实现错误处理的策略。
希望你能发现这篇帖子的信息量和帮助。你也可以查看RxJS的官方文档,深入了解RxJS的Observable模块。另外,我推荐Brian Troncone的learnrxjs.io,以获得对RxJS概念更直观的解释。
The postUsing Observables to transform data in TypeScriptappeared first onLogRocket Blog.