Created
July 18, 2020 21:47
-
-
Save leopic/d5354b046dba5cd7503d1658efc71aa8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import PlaygroundSupport | |
PlaygroundPage.current.needsIndefiniteExecution = true | |
// Taken from https://www.avanderlee.com/swift/asynchronous-operations/ | |
class AsyncOperation: Operation { | |
private let lockQueue = DispatchQueue(label: "com.leopicado.asyncop", attributes: .concurrent) | |
override var isAsynchronous: Bool { | |
return true | |
} | |
private var _isExecuting: Bool = false | |
override private(set) var isExecuting: Bool { | |
get { | |
return lockQueue.sync { () -> Bool in | |
return _isExecuting | |
} | |
} | |
set { | |
willChangeValue(forKey: "isExecuting") | |
lockQueue.sync(flags: [.barrier]) { | |
_isExecuting = newValue | |
} | |
didChangeValue(forKey: "isExecuting") | |
} | |
} | |
private var _isFinished: Bool = false | |
override private(set) var isFinished: Bool { | |
get { | |
return lockQueue.sync { () -> Bool in | |
return _isFinished | |
} | |
} | |
set { | |
willChangeValue(forKey: "isFinished") | |
lockQueue.sync(flags: [.barrier]) { | |
_isFinished = newValue | |
} | |
didChangeValue(forKey: "isFinished") | |
} | |
} | |
override func start() { | |
guard !isCancelled else { | |
finish() | |
return | |
} | |
isFinished = false | |
isExecuting = true | |
main() | |
} | |
override func main() { | |
fatalError("Subclasses must implement `main` without overriding super.") | |
} | |
func finish() { | |
isExecuting = false | |
isFinished = true | |
} | |
} | |
enum MyError: Error { | |
case error | |
} | |
class Op1: AsyncOperation { | |
var output: Result<[String], MyError>! { | |
didSet { | |
print("op1 done") | |
finish() | |
} | |
} | |
override func main() { | |
DispatchQueue.global().asyncAfter(deadline: .now() + 3.0) { | |
self.output = Bool.random() ? .success(["1", "2"]) : .failure(.error) | |
} | |
} | |
} | |
class Op2: AsyncOperation { | |
var input: Result<[String], MyError>! | |
var output: Result<Bool, MyError>! { | |
didSet { | |
print("op2 done") | |
finish() | |
} | |
} | |
override func main() { | |
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) { | |
guard case .success = self.input else { | |
self.output = .failure(.error) | |
return | |
} | |
self.output = .success(true) | |
} | |
} | |
} | |
let opQueue = OperationQueue() | |
let op1 = Op1() | |
let op2 = Op2() | |
// Connect both operations using a mediator | |
let adapterOperation = BlockOperation { | |
op2.input = op1.output | |
} | |
// The adapter needs op1 to finish before setting up op2 | |
adapterOperation.addDependency(op1) | |
// Op2 needs to be setup by the adapter | |
op2.addDependency(adapterOperation) | |
// Setup the queue | |
opQueue.addOperations([op1, op2, adapterOperation], waitUntilFinished: true) | |
print("op2.output?", try? op2.output.get()) | |
PlaygroundPage.current.finishExecution() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment