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

Swift 如何处理完成闭包中的多个错误,swift,closures,Swift,Closures,我正在实现一个函数,它接受一个数组和一个完成闭包。我迭代数组中的项目,根据每个项目执行网络请求。当每个请求完成时,它将其结果附加到集合中 一旦所有请求都返回,函数的完成块将使用累积的集合进行调用 我的问题是,我不确定如何处理网络请求可能提供的(可能是多个)错误。有没有标准的方法来处理这种模式?首先想到的想法是: 一旦一个网络请求的错误被给出,调用函数的完成闭包,结果为nil,错误为零 在所有网络请求都完成后,使用某种结构调用完成闭包,该结构包含成功完成的调用的结果和未成功完成的调用的错误 这

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

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

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

  • 一旦一个网络请求的错误被给出,调用函数的完成闭包,结果为nil,错误为零

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

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


我在SWIFT 3.1中实现这一点。

< P>我不知道你的完成闭包是如何看起来的,但是你可以考虑使用<代码>枚举> /COD>,结果().< /P>有一个相关值。 如果像这样创建
enum

enum ClosureResult {
   case success([String])
   case failure([String], [Error])
}
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)
    }
}
根据网络查找是失败还是成功,您可以返回不同的值。请注意,在我的
failure
案例中,我将传递一个
Error
对象数组作为第二个参数,我不知道这是否适合您,但这只是为了说明您可以传递多个参数作为回报

如果然后使用
closuresult
作为完成闭包的返回值,则可以在返回时在自己的块中对其执行
切换
,并根据该值执行操作

因此,您的函数可以如下所示:

enum ClosureResult {
   case success([String])
   case failure([String], [Error])
}
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对象中返回项的元组和该项的网络结果而不仅仅是字符串是否合理

是的,这是有道理的。我刚才使用了
String
Error
作为示例,但是您也可以使用自己的类

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

例如:

struct ClosureError {
    let item: String
    let error: Error
}
然后您的
closuresult
enum可以使用:

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) 
    }
    ...
最后,在
开关中
您将知道哪些项目失败:

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)"
        }
    }
} 
您可能应该为各个部分找到更好的名称:)


希望有意义,可以使用。

< p>我不知道你的完成闭包看起来如何,但是你可以考虑使用<代码>枚举< /Cord>。 如果像这样创建
enum

enum ClosureResult {
   case success([String])
   case failure([String], [Error])
}
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)
    }
}
根据网络查找是失败还是成功,您可以返回不同的值。请注意,在我的
failure
案例中,我将传递一个
Error
对象数组作为第二个参数,我不知道这是否适合您,但这只是为了说明您可以传递多个参数作为回报

如果然后使用
closuresult
作为完成闭包的返回值,则可以在返回时在自己的块中对其执行
切换
,并根据该值执行操作

因此,您的函数可以如下所示:

enum ClosureResult {
   case success([String])
   case failure([String], [Error])
}
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对象中返回项的元组和该项的网络结果而不仅仅是字符串是否合理

是的,这是有道理的。我刚才使用了
String
Error
作为示例,但是您也可以使用自己的类

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

例如:

struct ClosureError {
    let item: String
    let error: Error
}
然后您的
closuresult
enum可以使用:

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) 
    }
    ...
最后,在
开关中
您将知道哪些项目失败:

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)"
        }
    }
} 
您可能应该为各个部分找到更好的名称:)


希望这是有意义的,可以使用。

看看它,它看起来很有趣,可能是我最好的选择,但是如果我开始使用PromiseKit,那么我的功能的用户是否需要了解如何使用PromiseKit?我希望避免给用户增加任何额外的要求。我只是想知道,对于不使用PromiseKit的人来说,什么是正常的。这取决于您的要求,请咨询将使用此功能的人。另一种可能是创建操作,以便您可以构建依赖项等。我不确定您想要实现什么。看看它看起来很有趣,可能是我的最佳选择,但如果我开始使用PromiseKit,那么我的功能的用户是否需要了解如何使用PromiseKit?我希望避免给用户增加任何额外的要求。我只是想知道,对于不使用PromiseKit的人来说,什么是正常的。这取决于你的要求,请与使用此功能的人联系