我有一系列异步方法定义如下:
func step1(input: Step1InputData, completion: (Step1OutputData -> Void)) { /* do something */ }
func step2(input: Step1OutputData, completion: (Step2OutputData -> Void)) { /* do something */ }
// etc...
如您所见,step1
的输出是step2
的输入。这些类型都实现了StepData
协议:
protocol StepData {}
class Step1InputData : StepData { }
class Step1OutputData : StepData { }
class Step2OutputData : StepData { }
最后,我有这个自定义运算符:
infix operator => { associativity left }
func => <P:StepData, Q:StepData, R:StepData> (left:((P, Q -> Void) -> Void), right:((Q, R -> Void) -> Void)) -> ((P, R -> Void) -> Void) {
return { (x, completion) in
left(x, { y in
right(y, completion)
})
}
}
...这意味着我可以写下以下内容:
let combinedStep = step1 => step2
这很棒,因为它超级可读,至关重要它强制执行步骤之间的类型安全。
问题在于我希望能够保持combinedStep
的进度。例如,step1
可以是图像上传,step2
可以是本地存储写入。如果step2
失败,下次我尝试时,我想从我离开的地方继续,而不是重新上传图片。当然,可以将任意数量的步骤链接在一起。
我可以提出一个系统,它有一系列步骤并管理步骤之间的数据传递,并且能够保持进度,但是,我想不出办法在保持编译时类型安全的同时执行此操作。
具有更多功能编程经验的人能指出我正确的方向吗?
答案 0 :(得分:0)
我建议您研究PromiseKit,BrightFutures和ReactiveCocoa等框架。
这些框架与您的问题和解决方案空间相同,需要编写异步操作,并且它们都提供了多种方法来处理错误和重试。
如果您喜欢其中任何一种,您可以采用它们代替您的自定义实施,并利用大型开发人员社区的支持。或者你可以从他们的代码中找到灵感,然后再带回你的。
享受
答案 1 :(得分:0)
我认为您需要使运算符正确关联,并在完成处理程序中传递枚举以允许失败。
enum StepResult<Output> {
case Success(Output)
case Failure((StepResult<Output>->Void)->Void)
static func toFailHandlerWithInput<Input>(input: Input, processor: (Input,(StepResult<Output>)->Void)->Void) -> StepResult<Output> {
let handler : ((StepResult<Output>->Void)->Void) = { completion in
processor(input) { pout in
completion(pout)
}
}
return .Failure(handler)
}
}
infix operator => { associativity right }
func =><Input,Intermediate,Output>(left: (Input,(StepResult<Intermediate>)->Void)->Void, right: (Intermediate,(StepResult<Output>)->Void)->Void) -> (Input,(StepResult<Output>)->Void)->Void {
var mergedOp : ((Input,(StepResult<Output>)->Void)->Void)!
mergedOp = { input,completion in
left(input) { intermediate in
switch intermediate {
case .Success(let output):
right(output, completion)
case .Failure:
let failure = StepResult.toFailHandlerWithInput(input, processor: mergedOp)
completion(failure)
}
}
}
return mergedOp
}
var counter1 = 0
func step1(input: Int, completion: (StepResult<Double>)->Void) {
print("performing step 1...")
counter1 += 1
if ( counter1 > 1 ) {
print("Step 1 will succeed...")
let result = 2.0 * Double(input + counter1)
completion(.Success(result))
} else {
print("Step 1 fails...")
completion(StepResult.toFailHandlerWithInput(input, processor: step1))
}
}
var counter2 = 0
func step2(input: Double, completion: (StepResult<Double>)->Void) {
print("performing Step 2...")
counter2 += 1
if ( counter2 > 2 ) {
print("Step 2 will succeed...")
let result = 3 * input + Double(counter2)
completion(.Success(result))
} else {
print("Step 2 fails...")
completion(StepResult.toFailHandlerWithInput(input, processor: step2))
}
}
var counter3 = 0
func step3(input: Double, completion: (StepResult<Double>)->Void) {
print("performing Step 3...")
counter3 += 1
if ( counter3 > 1 ) {
print("Step 3 will succeed...")
let result = 4 * input + Double(counter3)
completion(.Success(result))
} else {
print("Step 3 fails...")
completion(StepResult.toFailHandlerWithInput(input, processor: step3))
}
}
func comboHandler(result: StepResult<Double>) {
switch result {
case .Success(let output):
print("output: \(output)")
case .Failure(let failHandler):
failHandler(comboHandler) // call again until success
}
}
let combinedSteps = step1 => step2 => step3
combinedSteps(5) { result in
comboHandler(result)
}
答案 2 :(得分:0)
我在这里提出的具体问题的答案,如果它对其他任何人有用,结果是对Memoization采取了类似的方法。严格来说,它是缓存而不是memoization,但是将step函数包装在另一个采用缓存键参数的函数中的概念是相同的。