Why does Combine's receive(on:) operator swallow errors?

家住魔仙堡 提交于 2020-03-25 22:39:35

问题


The following pipeline:

enum MyError: Error {
  case oops
}
let cancel = Fail<Int, Error>(error: MyError.oops)
  .print("1>")
  .print("2>")
  .sink(receiveCompletion: { status in
    print("status>", status)
  }) { value in
    print("value>", value)
}

Outputs:

1>: receive subscription: (Empty)
2>: receive subscription: (Print)
2>: request unlimited
1>: request unlimited
1>: receive error: (oops)
2>: receive error: (oops)
status> failure(__lldb_expr_126.MyError.oops)

The problem

However, if I insert a receive(on:) operator into the previous pipeline:

enum MyError: Error {
  case oops
}
let cancel = Fail<Int, Error>(error: MyError.oops)
  .print("1>")
  .receive(on: RunLoop.main)
  .print("2>")
  .sink(receiveCompletion: { status in
    print("status>", status)
  }) { value in
    print("value>", value)
}

the output is:

1>: receive subscription: (Empty)
1>: receive error: (oops)

The receive operator seems to short-circuit the pipeline. I haven't seen it happen for other publishers, just when I use a Fail or PassthroughSubject publisher.

Is this expected behavior? If so, what's the reason for it?


Workaround

Here's an example of creating a failing publisher that works with the receive(on:) publisher:

struct FooModel: Codable {
  let title: String
}

func failPublisher() -> AnyPublisher<FooModel, Error> {
  return Just(Data(base64Encoded: "")!)
    .decode(type: FooModel.self, decoder: JSONDecoder())
    .eraseToAnyPublisher()
}

let cancel = failPublisher()
  .print("1>")
  .receive(on: RunLoop.main)
  .print("2>")
  .sink(receiveCompletion: { status in
    print("status>", status)
  }) { value in
    print("value>", value)
}

回答1:


It's possible you're running into the same problem discussed in this post. Apparently receive(on:) will send all messages asynchronously via the given scheduler, including subscription messages. So what's happening is the error is sent before the the subscription event has a chance to be sent asynchronously, and so there is no subscriber attached to the receive publisher when the next event comes in.

However, it looks like they are changing this as of developer beta 1 of iOS 13.3:

As of developer beta 1 of iOS 13.3 (and associated releases for other platforms), we've changed the behavior of receive(on:) plus other Scheduler operators to synchronously send their subscription downstream. Previously, they would "async" it to the provided scheduler.



来源:https://stackoverflow.com/questions/58945952/why-does-combines-receiveon-operator-swallow-errors

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!