反应式设计:抛出与发布错误

Posted

技术标签:

【中文标题】反应式设计:抛出与发布错误【英文标题】:Reactive design: throw vs. publish error 【发布时间】:2017-05-23 11:48:52 【问题描述】:

下面的代码示例有 (Rx)Swift 的味道,但这个问题对于任何具有反应特性和抛出能力的语言都是通用的。

考虑一个返回可观察序列的函数,但它会在创建序列之前进行一些完整性检查。检查失败意味着序列不能产生值。

func yieldFoos() -> Observable<Foo> 
  guard isValid(internalFoo) else 
    // throw or return one shot observable?
  
  return createValidObservable(from: internalFoo)

在状态有效性检查失败的情况下,该函数应该抛出还是返回一个可观察的镜头,这将只产生一个错误?我的编码员的胆量想出了这些优点和缺点:

Throwing 在逻辑上感觉更干净(这是一个阻止 observable 创建的失败),但会导致繁琐的调用代码 - catch 块、不同执行范围内的多个错误处理点。

One shot observable 导致调用代码更短更清晰,但不知何故感觉不对。为了简洁起见,observable 被强制作为非顺序错误状态的载体。

有人有强烈的意见值得追随吗?还是另一种被忽视的优雅解决方案?

【问题讨论】:

【参考方案1】:

我想知道您是否认为 Observable 发出错误是错误的。这是它工作的一部分。

当您考虑它时,您的 createValidObservable(from:) 函数可能会发出错误,尽管传递给了一个有效的 internalFoo,因此调用 yieldFoos() 的代码无论如何都必须准备好处理发出的错误。您不妨将所有错误处理代码放在一起。我会更进一步,让您的 create 函数能够通过发出错误并取消此 yieldFoos 函数来处理无效的 foos 本身。

现在,如果您希望 yieldFoos() 返回 Driver 而不是 observable,那么您必须通过抛出或前置条件来处理错误(因为驱动程序不会发出错误。 )

func yieldFoos() -> Observable<Foo> 
    guard isValid(internalFoo) else 
        return Observable.error(myError)
    
    return createValidObservable(from: internalFoo)

我认为你需要克服你的感觉,即拥有一个立即返回错误的 Observable 是错误的。这对于 Observable 来说是一件非常有效的事情,并且所有使用该 Observable 的代码都需要准备好处理。

【讨论】:

Observable 发出错误并没有错。我只是觉得这样的错误应该是“可观察的”(无法继续序列),而不是首先创建可观察的。序列失败是正常的,创建失败是异常的不一致,因此抛出似乎是合适的。但是单发Observable.error运营商的存在暗示我可能只是想太多了。 如果你的 create 函数被传递了一个无效的 internalFoo,它将无法继续这个序列...... :-) 将 Observable 错误视为他们的 throw 版本可能会有所帮助,或者考虑它作为异步事件的抛出。它的工作原理就像一个投掷,它通过管道传播,直到它被处理...... 所以你会这样说 - 在反应式代码中,一般来说抛出的用例更少吗?我认为关键的反应性概念之一是一切都可以逐渐异步甚至懒惰,但您的转换代码不应该关心。而投掷本质上是同步的...... 不,这不太对……我是说 Rx 的 onError 事件类似于 throw。例如,如果您在 flatMapLatest 或任何其他 Rx 运算符中抛出,它将自动转换为 onError 事件。异常和错误事件是表示相同抽象的两种不同方式。是的,抛出是同步的,但错误事件也是同步的,因为它们会立即关闭可观察的链。 @PavelZdenek 我看到了您澄清的关于创建失败与序列失败的区别。您如何看待通过抛出创建 Observable 的错误和发出错误(分别)来区分它们?即yieldFoos() throws -&gt; Observable&lt;Foo&gt;,其中如果创建observable失败,则会抛出错误。但是,如果可观察序列中有错误,则它会以 onError 事件终止。这样就可以将 observable 的错误与序列错误区分开来。【参考方案2】:

你的函数应该是这样的:

func yieldFoos() -> Observable<Foo> 
    Observable.create  observer in 

        guard isValid(internalFoo) else 
            observer.onError(yourError)
        

        let subscription =  
            createValidObservable(from: internalFoo)
                .subscribe(onNext:  foo in
                    observer.onNext(foo)
                    observer.onCompleted()
                )
        return Disposables.create 
            // your dispose
            subscription.dispose()
        
    


然后,当你调用它时:

yieldFoos()
    .subscribe(
     onNext:  foo in
         // your code with foo
     ,
     onError:  error in
        // manage errors
     )
    .addDisposableTo(disposeBag)

【讨论】:

我知道如何构造和使用 observables,谢谢。我在问是否应该抛出异常故障或按顺序排列。你似乎喜欢序列,但没有推理。

以上是关于反应式设计:抛出与发布错误的主要内容,如果未能解决你的问题,请参考以下文章

关于java异常抛出与返回值

异常抛出与捕获的思考

Cloudinary openUploadWidget 在我的反应应用程序中抛出错误

反应路由抛出错误:无法读取浏览器路由器未定义的属性“推送”

反应:npm start 工作,npm run build 抛出 ELIFECYCLE 错误(errno 1)

为啥 npm start 会抛出 events.js:187 throw er; // 我的反应项目中未处理的“错误”事件?