服务发布结果合并数据失败

    科技2022-07-12  117

    服务发布结果合并数据失败

    One of Combine’s features that is somewhat painful to work with is its error mechanism. In Combine, publishers have an Output type and a Failure type. The Output represents the values that a publisher can emit, while the Failure represents the errors that a publisher can emit. This is really convenient because you know exactly what to expect from a publisher you subscribe to.

    Combine的错误机制是有些痛苦的功能之一。 在合并中,发布者具有Output类型和Failure类型。 Output代表发布者可以发出的值,而Failure代表发布者可以发出的错误。 这真的很方便,因为您确切知道订阅的发布者会有什么期望。

    But what happens when you have a slightly more complicated setup? What happens if you want to transform a publisher's output into a new publisher, but the errors of the old and new publishers don't line up?

    但是,如果您的设置稍微复杂一些会怎样? 如果您想将发布者的输出转换为新发布者,但新旧发布者的错误没有排列在一起,会发生什么?

    The other day, I was asked a question about this. The person wanted to know how they could write an extension on Publisher that would transform URLRequest values into URLSession.DataTaskPublisher values so that each emitted URLRequest would automatically become a network request. Here's what my initial experiment looked like (or rather, the code I would have liked to write):

    前几天,有人问我一个问题。 该人员想知道他们如何在Publisher上编写扩展程序,该扩展程序将URLRequest值转换为URLSession.DataTaskPublisher值,以便每个发出的URLRequest都将自动成为网络请求。 这是我最初的实验的样子(或更确切地说,是我想编写的代码):

    extension Publisher where Output == URLRequest { func performRequest() -> AnyPublisher<(data: Data, response: URLResponse), Error> { return self .flatMap({ request in return URLSession.shared.dataTaskPublisher(for: request) }) .eraseToAnyPublisher() } }

    Not bad, right? But this doesn’t compile. The following error appears in the console:

    还不错吧? 但这无法编译。 控制台中出现以下错误:

    instance method ‘flatMap(maxPublishers::)’ requires the types ‘Self.Failure’ and ‘URLSession.DataTaskPublisher.Failure’ (aka ‘URLError’) be equivalent

    In short, flatMap requires that the errors of the source publisher and the one I'm creating be the same. That's a bit of a problem because I don't know exactly what the source publisher's error is. I also don't know if/how I can map it to URLError or if I can map URLError to Self.Failure.

    简而言之, flatMap要求源发布者的错误和我要创建的错误相同。 这有点问题,因为我不确切知道源发布者的错误是什么。 我也不知道是否/如何我可以将它映射到URLError或者,如果我可以映射URLError到Self.Failure 。

    Luckily, we know that Publisher.Failure must conform to the Error protocol. This means that we can erase the error type completely and transform it into a generic Error instead with Combine's mapError(_:) operator:

    幸运的是,我们知道Publisher.Failure必须符合Error协议。 这意味着我们可以完全擦除错误类型,然后使用Combine的mapError(_:)运算符将其转换为通用Error :

    extension Publisher where Output == URLRequest { func performRequest() -> AnyPublisher<(data: Data, response: URLResponse), Error> { return self .mapError({ (error: Self.Failure) -> Error in return error }) .flatMap({ request in return URLSession.shared.dataTaskPublisher(for: request) .mapError({ (error: URLError) -> Error in return error }) }) .eraseToAnyPublisher() } }

    Note that I apply mapError(_:) to self, which is the source publisher, and to the URLSession.DataTaskPublisher that's created in the flatMap. This way, both publishers emit a generic Error rather than their specialized error. The upside is that this code compiled. The downside is that when we subscribe to the publisher created in performRequest, we'll need to figure out which error may have occurred.

    请注意,我将mapError(_:)应用于源发布者self和在flatMap创建的URLSession.DataTaskPublisher 。 这样,两个发布者都发出通用Error而不是其专门错误。 好处是,此代码已编译。 不利之处是,当我们订阅在performRequest创建的发布者时,我们需要找出可能发生的错误。

    An alternative to erasing the error completely could be to map any errors emitted by the source publisher to a failing URLRequest:

    完全消除错误的另一种方法是将源发布者发出的任何错误映射到失败的URLRequest :

    extension Publisher where Output == URLRequest { func performRequest() -> AnyPublisher<(data: Data, response: URLResponse), URLError> { return self .mapError({ (error: Self.Failure) -> URLError in return URLError(.badURL) }) .flatMap({ request in return URLSession.shared.dataTaskPublisher(for: request) }) .eraseToAnyPublisher() } }

    I like this solution a little bit better because we don’t lose all error information. The downside here is that we don’t know which error may have occurred upstream. Neither solution is ideal, but the point here is not for me to tell you which of these solutions is best for your app. The point is that you can see how to transform a publisher’s value using mapError(_:) to make it fit your needs.

    我更喜欢此解决方案,因为我们不会丢失所有错误信息。 这里的缺点是我们不知道上游可能发生了哪个错误。 这两种解决方案都不是理想的解决方案,但是这里的要点并不是让我告诉您哪种解决方案最适合您的应用。 关键是您可以看到如何使用mapError(_:)转换发布者的价值以使其适合您的需求。

    Before I wrap up this quick tip, I want to show you an extension that you can use to transform the output of any publisher into a generic Error:

    在结束本快速提示之前,我想向您展示一个扩展,您可以使用该扩展将任何发布者的输出转换为通用Error :

    extension Publisher { func genericError() -> AnyPublisher<Self.Output, Error> { return self .mapError({ (error: Self.Failure) -> Error in return error }).eraseToAnyPublisher() } }

    You could use this extension as follows:

    您可以按以下方式使用此扩展名:

    extension Publisher where Output == URLRequest { func performRequest() -> AnyPublisher<(data: Data, response: URLResponse), Error> { return self .genericError() .flatMap({ request in return URLSession.shared.dataTaskPublisher(for: request) .genericError() }) .eraseToAnyPublisher() } }

    It’s not much, but it saves a few lines of code. Be careful when using this operator, though. You lose all error details from upstream publishers in favor of slightly better composability.

    它虽然不多,但可以节省几行代码。 但是,使用此运算符时要小心。 您会丢失上游发布者的所有错误详细信息,而倾向于更好的可组合性。

    Personally, I think your code will be more robust when you transform errors to the error that’s needed downstream, as I did in the second example. It makes sure that you explicitly handle any errors rather than ignoring them.

    就个人而言,我认为将错误转换为下游所需的错误时,您的代码将更加健壮,就像我在第二个示例中所做的那样。 它确保您显式处理任何错误,而不是忽略它们。

    翻译自: https://medium.com/better-programming/how-to-change-a-publishers-failure-type-in-combine-bbe6049ff8e3

    服务发布结果合并数据失败

    Processed: 0.011, SQL: 8