V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rabbbit
V2EX  ›  程序员

RxJS 为什么设计成抛出错误后直接关闭 Observer,不再发出值?实际的业务代码都是怎么处理异常错误的?

  •  1
     
  •   rabbbit · 2023-10-20 20:30:46 +08:00 · 1695 次点击
    这是一个创建于 395 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如下面这段代码

    fromEvent(document, 'click').pipe(
      map(i => {
        console.log('click')
        throw new Error('error')
      })
    ).subscribe({
      next: console.log,
      complete: () => console.log('complete'),
      error: (err) => console.log(err)
    })
    

    我以为的输出:点一下输出 click 和 error ,再点还输出 click 和 error
    实际上的输出:点一下输出 click 和 error ,再点就没反应了
    就算用 catchError 也一样会被关闭

    事件倒还好,加个 retry 就行了,of 之类还得用 map 包起来
    例如想输出 1 3 不能这么写

    of(1, 2, 3).pipe(
      map(i => {
        if (i === 2) {
          throw new Error('error')
        }
        return i;
      }),
      catchError(err => EMPTY)
    ).subscribe({
      next: console.log,
      complete: () => console.log('complete'),
      error: (err) => console.log(err)
    })
    

    要这么写用 map 包起来

    of(1, 2, 3).pipe(
      switchMap(i => {
        return of(i).pipe(
          map(i => {
            if (i === 2) {
              throw new Error('error')
            }
            return i;
          }),
          catchError(i => EMPTY)
        )
      })
    ).subscribe({
      next: console.log,
      complete: () => console.log('complete'),
      error: (err) => console.log(err)
    })
    

    为啥要这么设计?出来个错误把整个管子都给扬了。
    RxJS 实际的业务都是怎么写的,都是加 retry 或者拿 map 包起来吗?总不能在所有 map 里写 try catch 吧。

    第 1 条附言  ·  2023-10-20 22:44:18 +08:00

    补充一下,其实用 catchError 也可以,但是只有fromEvent 事件这类的才行。

    fromEvent(document, 'click').pipe(
      map(() => {
        console.log('click')
        throw new Error('error')
      }),
      catchError((err, caught) => caught)
    ).subscribe({
      next: console.log,
      complete: () => console.log('complete'),
      error: (err) => console.log(err)
    })
    
    第 2 条附言  ·  2023-10-21 21:17:34 +08:00

    封装一个pipe供使用(供参考未测试,不清楚是否通用)

    另附各种跳过继续运行的尝试解决方案参考
    https://iamturns.medium.com/continue-rxjs-streams-when-errors-occur-c6a031f9a6cf

    function tryCatch(tryPipe, catchCallback) {
      return pipe(
        switchMap(i => of(i).pipe(tryPipe, catchError(err => catchCallback(err))))
      )
    }
    
    of(1, 2, 3).pipe(
      tryCatch(
        pipe(
          map(i => {
            if (i === 2) {
              throw new Error('err')
            }
            return i
          })
        ),
        err => EMPTY,
      )
    ).subscribe({
      next: console.log,
      complete: () => console.log('complete'),
      error: (err) => console.log('error', err)
    })
    // 输出 1 3
    
    6 条回复    2023-10-23 10:15:04 +08:00
    66450146
        1
    66450146  
       2023-10-20 20:54:07 +08:00 via iPhone
    如果有这个需要的话,把元素弄成 Result 不就得了

    https://github.com/badrap/result
    Magentaize
        2
    Magentaize  
       2023-10-21 09:31:57 +08:00 via iPhone   ❤️ 1
    materialize 了解一下
    vaporSpace
        3
    vaporSpace  
       2023-10-21 11:12:00 +08:00
    ``` js
    of(1, 2, 3)
    .pipe(
    switchMap((n) =>
    of(n).pipe(
    map((o) => {
    if (o === 2) {
    throw 'error';
    }
    return o;
    }),
    catchError(() => of('EMPTY'))
    )
    )
    )
    .subscribe((x) => console.log(x));
    ```

    达到效果,就是有点丑
    yor1g
        4
    yor1g  
       2023-10-21 12:40:15 +08:00
    EMPTY 直接走 complete 了吧 catch 返回正常值才能不会断流
    Helsing
        5
    Helsing  
       2023-10-21 15:09:32 +08:00 via iPhone   ❤️ 1
    二楼说的 materialize 和 dematerialize 操作符是比较好处理方案
    DingJZ
        6
    DingJZ  
       2023-10-23 10:15:04 +08:00
    借楼问个问题,怎么能把 rx 代入日常的开发中,学了 n 次了,基本上每次用的时候现学现写,后面就忘了,下次再重来
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1037 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:30 · PVG 06:30 · LAX 14:30 · JFK 17:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.