NSOperationQueue如何等待两个异步操作?

时间:2014-12-02 21:45:00

标签: ios nsoperationqueue

如何让NSOperationQueue(或其他任何东西)等待两个带回调的异步网络调用?流程需要看起来像这样

Block Begins {
    Network call with call back/block begins {
        first network call is done 
    }
}
Second Block Begins {
    Network call with call back/block begins {
        second network call is done 
    }
} 

Only run this block once the NETWORK CALLS are done {
    blah
}

这是我到目前为止所拥有的。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
__block NSString *var;


[queue addOperation:[NSBlockOperation blockOperationWithBlock:^{
   [AsyncReq get:^{
       code
    } onError:^(NSError *error) {
       code
    }];
}]];

[queue addOperation:[NSBlockOperation blockOperationWithBlock:^{
   [AsyncReq get:^{
       code
    } onError:^(NSError *error) {
       code
    }];
}]];
[queue waitUntilAllOperationsAreFinished];
//do something with both of the responses

3 个答案:

答案 0 :(得分:17)

你必须使用NSOperation Queue吗?以下是您如何使用调度组执行此操作:

dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
[AsyncReq get:^{
    code
    dispatch_group_leave(group); 
} onError:^(NSError *error) {
    code
    dispatch_group_leave(group);
}];


dispatch_group_enter(group);
[AsyncReq get:^{
    code
    dispatch_group_leave(group); 
} onError:^(NSError *error) {
    code
    dispatch_group_leave(group);
}];

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"Both operations completed!")
});

答案 1 :(得分:7)

使用Grand Central Dispatch和DispatchGroup

使用Swift 3,在最简单的情况下,您不需要对任务状态进行细粒度控制,您可以使用Grand Central Dispatch和DispatchGroup。以下Playground代码显示了它的工作原理:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let group = DispatchGroup()

group.enter()
// Perform some asynchronous operation
let queue1 = DispatchQueue(label: "com.example.imagetransform")
queue1.async {
    print("Task One finished")
    group.leave()
}

group.enter()
// Perform some asynchronous operation
let queue2 = DispatchQueue(label: "com.example.retrievedata")
queue2.async {
    print("Task Two finished")
    group.leave()
}

group.notify(queue: DispatchQueue.main, execute: { print("Task Three finished") })

一旦两个异步任务都完成,前面的代码将打印"Task Three finished"

使用OperationQueueOperation

OperationQueueOperation用于您的请求任务需要更多样板代码,但提供许多优势,例如kvo ed状态和依赖。

1。创建一个Operation子类,它将充当抽象类

import Foundation

/**
 NSOperation documentation:
 Operation objects are synchronous by default.
 At no time in your start method should you ever call super.
 When you add an operation to an operation queue, the queue ignores the value of the asynchronous property and always calls the start method from a separate thread.
 If you are creating a concurrent operation, you need to override the following methods and properties at a minimum:
 start, asynchronous, executing, finished.
 */

open class AbstractOperation: Operation {

    @objc enum State: Int {
        case isReady, isExecuting, isFinished

        func canTransition(toState state: State) -> Bool {
            switch (self, state) {
            case (.isReady, .isExecuting):      return true
            case (.isReady, .isFinished):       return true
            case (.isExecuting, .isFinished):   return true
            default:                            return false
            }
        }
    }

    // use the KVO mechanism to indicate that changes to `state` affect other properties as well
    class func keyPathsForValuesAffectingIsReady() -> Set<NSObject> {
        return [#keyPath(state) as NSObject]
    }

    class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> {
        return [#keyPath(state) as NSObject]
    }

    class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> {
        return [#keyPath(state) as NSObject]
    }

    // A lock to guard reads and writes to the `_state` property
    private let stateLock = NSLock()

    private var _state = State.isReady
    var state: State {
        get {
            stateLock.lock()
            let value = _state
            stateLock.unlock()
            return value
        }
        set (newState) {
            // Note that the KVO notifications MUST NOT be called from inside the lock. If they were, the app would deadlock.
            willChangeValue(forKey: #keyPath(state))

            stateLock.lock()
            if _state == .isFinished {
                assert(_state.canTransition(toState: newState), "Performing invalid state transition from \(_state) to \(newState).")
                _state = newState
            }
            stateLock.unlock()

            didChangeValue(forKey: #keyPath(state))
        }
    }

    override open var isExecuting: Bool {
        return state == .isExecuting
    }

    override open var isFinished: Bool {
        return state == .isFinished
    }

    var hasCancelledDependencies: Bool {
        // Return true if this operation has any dependency (parent) operation that is cancelled
        return dependencies.reduce(false) { $0 || $1.isCancelled }
    }

    override final public func start() {
        // If any dependency (parent operation) is cancelled, we should also cancel this operation
        if hasCancelledDependencies {
            finish()
            return
        }

        if isCancelled {
            finish()
            return
        }

        state = .isExecuting
        main()
    }

    open override func main() {
        fatalError("This method has to be overriden and has to call `finish()` at some point")
    }

    open func didCancel() {
        finish()
    }

    open func finish() {
        state = .isFinished
    }

}

2。创建您的运营

import Foundation

open class CustomOperation1: AbstractOperation {

    override open func main() {
        if isCancelled {
            finish()
            return
        }

        // Perform some asynchronous operation
        let queue = DispatchQueue(label: "com.app.serialqueue1")
        let delay = DispatchTime.now() + .seconds(5)
        queue.asyncAfter(deadline: delay) {
            self.finish()
            print("\(self) finished")
        }
    }

}
import Foundation

open class CustomOperation2: AbstractOperation {

    override open func main() {
        if isCancelled {
            finish()
            return
        }

        // Perform some asynchronous operation
        let queue = DispatchQueue(label: "com.app.serialqueue2")
        queue.async {
            self.finish()
            print("\(self) finished")
        }
    }

}

3。使用

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

// Declare operations
let operation1 = CustomOperation1()
let operation2 = CustomOperation2()
let operation3 = CustomOperation1()

// Set operation3 to perform only after operation1 and operation2 have finished
operation3.addDependency(operation2)
operation3.addDependency(operation1)

// Launch operations
let queue = OperationQueue()
queue.addOperations([operation2, operation3, operation1], waitUntilFinished: false)

使用此代码,operation3始终保证最后执行。

您可以在此GitHub repo找到此游乐场。

答案 2 :(得分:0)

AsyncOperation将其状态保持为Operation自己的状态。  由于操作具有异步性质。我们需要明确定义操作状态。  因为异步操作的执行在调用后立即返回。  因此,在AsyncOperation的子类中,您只需要在完成处理程序中将操作的状态设置为完成

class AsyncOperation: Operation {

public enum State: String {
   case ready, executing, finished

    //KVC of Operation class are
    // isReady, isExecuting, isFinished
    var keyPath: String {
        return "is" + rawValue.capitalized
    }
}

//Notify KVO properties of the new/old state
public var state = State.ready {
    willSet {
        willChangeValue(forKey: newValue.keyPath)
        willChangeValue(forKey: state.keyPath)
    }
    didSet{
        didChangeValue(forKey: oldValue.keyPath)
        didChangeValue(forKey: state.keyPath)
    }
  }
}



extension AsyncOperation {

//have to make sure the operation is ready to maintain dependancy with other operation
//hence check with super first
override open var isReady: Bool {
    return super.isReady && state == .ready
}

override open var isExecuting: Bool {
    return state == .executing
}

override open var isFinished: Bool {
    return state == .finished
}

override open func start() {
    if isCancelled {
        state = .finished
        return
    }

    main()
    state = .executing
}

override open func cancel() {
    super.cancel()
    state = .finished
}  }

现在可以通过您自己的Operation类进行呼叫

Class MyOperation: AsyncOperation {


     request.send() {success: { (dataModel) in
      //waiting for success closure to be invoked before marking the state as completed
           self.state = .finished
     } }