引言
RxJS(Reactive Extensions for JavaScript)是一种全新的异步编程方式,它通过一系列的操作符来简化异步操作,从而让我们更容易地编写自己所需的代码。然而,由于 RxJS 中处理异常的方式与传统的 JavaScript 不同,许多人在使用 RxJS 时会遇到一些难以解决的问题。本文将介绍 RxJS 中的一些 Error Handling 小贴士,帮助大家更好地处理 RxJS 中的异常情况。
基本概念
在 RxJS 中,异常是一种特殊的事件,在 Observable 序列中,异常通常是通过 Observable 回调函数的 error 参数来传递的。常见的 Observable 包括 ajax
、timer
、interval
、from
等,对于这些 Observable,我们可以通过 subscribe
方法来订阅它们并接收它们所发出的事件,包括值、完成和异常。
举个例子,下面的代码使用 ajax
创建一个 Observable,该 Observable 向服务器发送请求并返回一个 Response 对象。在 subscribe
方法中,我们为 Observable 注册了一个回调函数,该回调函数会在 Observable 发出一个值、完成或者异常时被调用:
import { ajax } from 'rxjs/ajax'; const obs = ajax('https://jsonplaceholder.typicode.com/todos/1'); obs.subscribe( resp => console.log(resp), err => console.error(err), () => console.log('completed') );
上面的代码中,我们使用 console.log
打印了在 ajax
执行过程中返回的 Response 对象,并在异常时使用 console.error
打印了异常信息。当 ajax
执行完毕时,我们还通过 console.log
打印了一个 "completed" 消息。
捕获异常
当一个 Observable 发生异常时,我们可以使用 catchError
操作符来捕获并处理异常。catchError
操作符会返回一个新的 Observable,该 Observable 会发出一个异常事件,并在异常发生时能够调用我们自己指定的错误处理函数。
举个例子,下面的代码中,我们使用 ajax
创建一个 Observable,同时使用 catchError
操作符来捕获异常,并使用 of
操作符创建一个新的 Observable 来发出一个默认值:
// javascriptcn.com 代码示例 import { ajax } from 'rxjs/ajax'; import { catchError, of } from 'rxjs'; const obs = ajax('https://jsonplaceholder.typicode.com/todos/1'); obs.pipe( catchError(err => { console.error(err); return of({ error: 'default value' }); }) ).subscribe( resp => console.log(resp), err => console.error(err), () => console.log('completed') );
上面的代码中,在 catchError
中捕获到异常后,我们使用 console.error
打印了异常信息,并通过 of
创建了一个新的 Observable,该 Observable 发出一个对象,其中包含一个错误信息。在 subscribe
方法中,我们分别处理了新的 Observable 的值、错误和完成事件。
抛出异常
在 RxJS 中,我们可以使用 throwError
操作符来抛出一个异常。throwError
操作符会返回一个新的 Observable,该 Observable 会发出一个异常事件,并在异常发生时能够调用我们自己指定的错误处理函数。
举个例子,下面的代码中,在 filter
操作符中,我们使用 throwError
操作符抛出了一个异常,并使用 catchError
操作符来捕获这个异常:
// javascriptcn.com 代码示例 import { from, throwError } from 'rxjs'; import { catchError, filter } from 'rxjs/operators'; const obs = from([1, 2, 3]); obs.pipe( filter(n => { if (n === 2) { return throwError('error occurred'); } return true; }), catchError(err => { console.error(err); return throwError('default value'); }) ).subscribe( val => console.log(val), err => console.error(err), () => console.log('completed') );
上面的代码中,在 filter
操作符中我们判断当前值是否等于 2,如果是,则使用 throwError
抛出一个异常。在 catchError
中,我们捕获这个异常,并使用 console.error
打印异常信息,并返回一个新的 Observable,发出一个错误消息。在 subscribe
方法中,我们分别处理了新的 Observable 的值、错误和完成事件。
处理多个异常
在 RxJS 中,我们可以使用 catchError
操作符来处理单个异常,但是如果需要处理多个异常,我们可以使用 catch
操作符。catch
操作符和 catchError
操作符很相似,但是 catch
操作符可以处理多个异常,并且可以根据异常类型来分别处理不同的异常。
举个例子,下面的代码中,我们使用 from
创建一个 Observable,该 Observable 会发出一个字符串数组。在 mergeMap
操作符中,我们使用一个异步函数来将数组中的字符串转换为数字,但是如果字符串无法转换为数字,则会发生异常。在 catch
操作符中,我们分别处理类型为 RangeError
和类型为 SyntaxError
的异常:
// javascriptcn.com 代码示例 import { from, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; const obs = from(['1', '2', '3', 'a', 'b', 'c']); obs.pipe( mergeMap(str => { return of(parseInt(str, 10)).pipe( map(val => { if (isNaN(val)) { throw new RangeError('value is not a number'); } return val; }) ); }), catchError(err => { if (err instanceof RangeError) { console.error('range error occurred'); } else if (err instanceof SyntaxError) { console.error('syntax error occurred'); } else { console.error('unknown error occurred'); } return of(-1); }) ).subscribe( val => console.log(val), err => console.error(err), () => console.log('completed') );
上面的代码中,在 mergeMap
操作符中,我们使用 of
创建一个新的 Observable,并使用 map
操作符将字符串转换为数字。在 map
的回调函数中,我们使用 isNaN
判断当前值是否为数字,如果不是,则使用 throw new RangeError('value is not a number')
抛出一个类型为 RangeError
的异常。在 catch
操作符中,我们分别处理类型为 RangeError
和类型为 SyntaxError
的异常,并使用 return of(-1)
返回一个新的 Observable,发出一个数字值。在 subscribe
方法中,我们分别处理新的 Observable 的值、错误和完成事件。
总结
在 RxJS 中,处理异常是非常重要的一项技能,掌握好 Error Handling 的小贴士能够帮助我们更好地使用 RxJS,避免代码中出现难以解决的错误。在本文中,我们介绍了 RxJS 中处理异常的基本方法,包括捕获异常、抛出异常和处理多个异常,希望本文能够对大家学习和使用 RxJS 有所帮助。
示例代码
完整示例代码请参考下面的 Gist:
https://gist.github.com/jerrybaojin/cf75744019ad68a9174ab4f9ac1ce4d1
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65353cbe7d4982a6ebb953bc