如何处理完成闭包中的多个错误

Posted

技术标签:

【中文标题】如何处理完成闭包中的多个错误【英文标题】:How to handle with a multiple errors in a completion closure 【发布时间】:2017-05-23 10:49:45 【问题描述】:

我正在实现一个函数,它需要一个数组和一个完成闭包。我遍历数组中的项目,根据每个项目执行网络请求。当每个请求完成时,它会将其结果附加到一个集合中。

一旦所有请求都返回,函数的完成块将使用累积的集合调用。

我的问题是我不确定如何处理网络请求可能提供的(可能是多个)错误。有处理这种模式的标准方法吗?首先想到的是:

一旦给出网络请求的错误,就调用函数的完成闭包,结果为 nil 并且出现该错误。

在所有网络请求都完成后,使用某种结构调用完成闭包,该结构包含成功完成的调用的结果和未成功完成的调用的错误。

李>

这些选项似乎都不理想。有没有标准的方法来处理这个问题?我不确定这个函数的用户会合理地期待什么。

我正在 Swift 3.1 中实现这个。

【问题讨论】:

看看PromiseKit 它看起来很有趣,可能是我自己的最佳选择,但如果我开始使用 PromiseKit,我的函数的用户是否需要了解如何使用 PromiseKit?我希望避免为用户添加任何额外的要求。我只是想知道对于不使用 PromiseKit 的人来说什么是正常的。 这取决于您的要求,请咨询将使用此功能的人。另一种可能性是创建操作,以便您可以构建依赖项等。我不确定您想要实现什么。 【参考方案1】:

我不知道您的完成闭包看起来如何,但您可以考虑使用带有关联值的 enum 结果 (here you can read more about associated values)。

如果您像这样创建enum

enum ClosureResult 
   case success([String])
   case failure([String], [Error])

您可以根据网络查找是失败还是成功返回不同的值。请注意,在我的 failure 案例中,我将一组 Error 对象作为第二个参数传递,我不知道这是否适合您,但这只是为了说明您可以传递多个参数作为回报.

如果您随后使用 ClosureResult 作为完成闭包的返回值,则可以在返回时在自己的块中对其执行 switch 并根据该值采取行动。

所以,你的函数可能看起来像这样:

func yourFunction(items: [String], completion: (ClosureResult) -> Void) 
    var completedItems = [String]()
    var failedItems = [Error]()
    for item in items 
        //Do your network magic here 
        //if it succeeds add it to completedItems
        //if it fails, add it to failedItems
    

    //did we encounter any failures?
    if failedItems.count > 0 
        //yes we did, return a .failure then
        let failure = ClosureResult.failure(completedItems, failedItems)
        completion(failure)
     else 
        //no...return .success
        let success = ClosureResult.success(completedItems)
        completion(success)
    

然后你可以像这样使用它:

let items = ["a", "b", "c"]
yourFunction(items: items)  result in
    switch result 
    case .success(let okItems):
        print(okItems)
    case .failure(let okItems, let failedItems):
        print(failedItems)
    
 

更新

在你的评论中你问:

我唯一担心的是,在您的示例中,网络请求在“a”和“c”上成功但不是“b”,关闭结果包含有关哪个项目失败的任何信息。在 ClosureResult 对象中返回项目的元组和该项目的网络结果而不只是字符串是否合理?

是的,这是有道理的。我只是以StringError 为例,但您也可以使用自己的类。

我想,我要做的是,我会创建一个简单的类并返回它,而不是使用元组。

例如:

struct ClosureError 
    let item: String
    let error: Error

然后你的 ClosureResult 枚举可以使用它:

enum ClosureResult 
   case success([String])
   case failure([String], [ClosureError])

在您的yourFunction 中遇到故障时,您需要创建ClosureError 的新实例:

    ...
    var failedItems = [ClosureError]()
    ...
    for item in items 
        //Do your network magic here 
        //if it succeeds add it to completedItems
        //if it fails, add it to failedItems
        let error = //create the error
        let closureFailure = ClosureFailure(item, error)
        failedItems.append(closureFailure) 
    
    ...

最后,在您的switch 中,您会知道哪些项目失败了:

let items = ["a", "b", "c"]
yourFunction(items: items)  result in
    switch result 
    case .success(let okItems):
        print(okItems)
    case .failure(let okItems, let failedItems):
        for closureError in failedItems 
            print("item: \(closureError.item) has failed with error \(closureError.error)"
        
    
 

您可能应该为各个部分找出一些更好的名称:)

希望有意义并且可以使用。

【讨论】:

谢谢。这看起来既是最干净也是最好的答案。我唯一担心的是,在您的示例中,网络请求在“a”和“c”而不是“b”上成功,关闭结果包含有关哪个项目失败的任何信息。在 ClosureResult 对象中返回项目的元组和该项目的网络结果而不仅仅是字符串是否合理?非常感谢。 @JoshParadroid 好主意...我已经更新了我的答案,希望对你有意义:)

以上是关于如何处理完成闭包中的多个错误的主要内容,如果未能解决你的问题,请参考以下文章

你应该如何处理 UIAlertAction 的闭包参数

前端知识体系:JavaScript基础-作用域和闭包-如何处理循环的异步操作

Javascript 闭包 vs PHP 闭包,有啥区别?

Java:如何处理 servlet 中的多个会话 [重复]

如何使用 jsdoc-toolkit 记录匿名函数(闭包)

微信小程序闭包