这是一个通用解决方案,可应用于任何没有参数的异步函数,回调函数除外。我通过只有success 和failure 回调来简化逻辑,progress 应该不难添加。
所以,假设你的函数是这样的:
func startUploading(success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
DDLogDebug("JogUploader: Creating jog: \(self.jog)")
API.sharedInstance.createJog(self.jog,
failure: { error in
failure(error)
}, success: {_ in
success()
})
}
匹配的retry 函数可能如下所示:
func retry(times: Int, task: @escaping(@escaping () -> Void, @escaping (Error) -> Void) -> Void, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
task(success,
{ error in
// do we have retries left? if yes, call retry again
// if not, report error
if times > 0 {
retry(times - 1, task: task, success: success, failure: failure)
} else {
failure(error)
}
})
}
并且可以这样调用:
retry(times: 3, task: startUploading,
success: {
print("Succeeded")
},
failure: { err in
print("Failed: \(err)")
})
如果一直失败,上面将重试startUploading调用3次,否则将在第一次成功时停止。
编辑。具有其他参数的函数可以简单地嵌入到闭包中:
func updateUsername(username: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
...
}
retry(times: 3, { success, failure in updateUsername(newUsername, success, failure) },
success: {
print("Updated username")
},
failure: {
print("Failed with error: \($0)")
}
)
更新 retry 函数声明中的太多 @escaping 子句可能会降低其可读性,并增加使用函数时的认知负担。为了改善这一点,我们可以编写一个具有相同功能的简单通用结构:
struct Retrier<T> {
let times: UInt
let task: (@escaping (T) -> Void, @escaping (Error) -> Void) -> Void
func callAsFunction(success: @escaping (T) -> Void, failure: @escaping (Error) -> Void) {
let failureWrapper: (Error) -> Void = { error in
// do we have retries left? if yes, call retry again
// if not, report error
if times > 0 {
Retrier(times: times - 1, task: task)(success: success, failure: failure)
} else {
failure(error)
}
}
task(success, failureWrapper)
}
func callAsFunction(success: @escaping () -> Void, failure: @escaping (Error) -> Void) where T == Void {
callAsFunction(success: { _ in }, failure: failure)
}
}
由于是可调用的,结构体可以像普通函数一样被调用:
Retrier(times: 3, task: startUploading)(success: { print("success: \($0)") },
failure: { print("failure: \($0)") })
,或者可以通过app进行循环:
let retrier = Retrier(times: 3, task: startUploading)
// ...
// sometime later
retrier(success: { print("success: \($0)") },
failure: { print("failure: \($0)") })