paint-brush
OperationQueue + Code asynchrone : tout ce que vous devez savoirby@bugorbn
488
488

OperationQueue + Code asynchrone : tout ce que vous devez savoir

Boris Bugor5m2024/03/17
Read on Terminal Reader

Dans Swift, utiliser une file d'attente d'opérations pour du code synchrone peut sembler un pur enfer car, sous le capot, le code est considéré comme complet si la compilation de son code est terminée. Au moment où le code asynchrone sera exécuté, l'opération elle-même sera déjà terminée. Pour comprendre comment résoudre le problème, vous devez comprendre comment fonctionne le cycle de vie de l’opération.
featured image - OperationQueue + Code asynchrone : tout ce que vous devez savoir
Boris Bugor HackerNoon profile picture

Dans Swift, utiliser OperationQueue pour du code asynchrone peut sembler un pur enfer car, sous le capot, Operations sont considérées comme terminées si la compilation de leur code synchrone est terminée.


En d'autres termes, la compilation de l'exemple décrit ci-dessous produira un ordre d'exécution rompu puisque, au moment où le code asynchrone sera exécuté, l' Operation elle-même sera déjà terminée.


 let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.addOperation { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { print("First async operation complete") } print("First sync operation complete") } operationQueue.addOperation { DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { print("Second async operation complete") } print("Second sync operation complete") }


Ce code imprimera :


 First sync operation complete Second sync operation complete First async operation complete Second async operation complete


Il existe cependant un moyen de contourner ces restrictions. Pour comprendre comment résoudre le problème, vous devez comprendre comment fonctionne Operation sous le capot.


L' Operation elle-même comporte quatre indicateurs grâce auxquels vous pouvez suivre le cycle de vie de l'opération :


  • isReady — indique si l' Operation peut être effectuée à ce moment-là.


  • isExecuting indique si une Operation est actuellement en cours.


  • isFinished : indique si l' Operation est actuellement terminée.


  • isCancelled : indique si l' Operation a été annulée.


En théorie, l' Operation entre dans l'état isFinished avant que l' Operation elle-même ne soit exécutée de manière asynchrone, nous devons donc développer une technique par laquelle nous pourrons manipuler le cycle de vie de l' Operation .


Cette possibilité peut être résolue en sous-classant l' Operation et également en redéfinissant les méthodes start / cancel , ainsi que tous les drapeaux sur lesquels est construit le cycle de vie de l'opération.


Voici le code :


 public class AsyncOperation: Operation { // MARK: Open override open var isAsynchronous: Bool { true } override open var isReady: Bool { super.isReady && self.state == .ready } override open var isExecuting: Bool { self.state == .executing } override open var isFinished: Bool { self.state == .finished } override open func start() { if isCancelled { state = .finished return } main() state = .executing } override open func cancel() { super.cancel() state = .finished } // MARK: Public public enum State: String { case ready case executing case finished // MARK: Fileprivate fileprivate var keyPath: String { "is" + rawValue.capitalized } } public var state = State.ready { willSet { willChangeValue(forKey: newValue.keyPath) willChangeValue(forKey: state.keyPath) } didSet { didChangeValue(forKey: oldValue.keyPath) didChangeValue(forKey: state.keyPath) } } }


La sous-classe que nous avons reçue de l' Operation est basique et nous permet de la compléter manuellement avec force.


Pour travailler avec des blocs de complétion, vous devez créer une autre sous-classe. Cependant, ce ne sera pas une sous-classe de Operation , mais de AsyncOperation .


 public typealias VoidClosure = () -> Void public typealias Closure<T> = (T) -> Void public class CompletionOperation: AsyncOperation { // MARK: Lifecycle public init(completeBlock: Closure<VoidClosure?>?) { self.completeBlock = completeBlock } // MARK: Public override public func main() { DispatchQueue.main.async { [weak self] in self?.completeBlock? { DispatchQueue.main.async { self?.state = .finished } } } } // MARK: Private private let completeBlock: Closure<VoidClosure?>? }


Cette sous-classe nous permettra de passer une fermeture à l' Operation , après quoi l' Operation sera terminée.


Essayons ce type d'opération en pratique :


 let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.addOperation( CompletionOperation { completion in DispatchQueue.main.asyncAfter(deadline: .now() + 1) { print("First async operation complete") completion?() } print("First sync operation complete") } ) operationQueue.addOperation( CompletionOperation { completion in DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { print("Second async operation complete") completion?() } print("Second sync operation complete") } )


En conséquence, nous avons pu réaliser une exécution synchrone des Operations :


 First sync operation complete First async operation complete Second sync operation complete Second async operation complete


N'hésitez pas à me contacter au Twitter si vous avez des questions. De plus, vous pouvez toujours achète-moi un café .


Également publié ici