在继续处理下一个请求或调度队列中的下一个 DispatchWorkItem 之前,如何等待接收来自 DispatchWorkItem 的响应

Big*_*ire 1 dispatch grand-central-dispatch dispatch-async swift dispatch-queue

我有一组调度工作项,如何等到一项工作完成后再继续队列中的下一项工作?

func AsyncCalls(statusHandler: @escaping (String) -> Void){

    var dispatchWorkItems : [DispatchWorkItem] = []
    let categoryWorkItem = DispatchWorkItem {
        main {
        return  statusHandler("Loading categories ")
        }
        self.modelView.getCategories(completion: { data,error in
            main {
            if data.isEmpty {
            return  statusHandler("\(error )")
            }else{
            return  statusHandler("Done loading categories")
            }
            }
        })
    }

    let itemsWorkItem = DispatchWorkItem {
        main {
            return statusHandler("Loading Inventory ")
        }
        self.modelView.getInventory(completion: { data,error in
            main {
                if data.isEmpty {
                return  statusHandler("\(error )")
                }else{
                return  statusHandler("Done loading Inventory")
                }
            }
        })
    }


    dispatchWorkItems.append(categoryWorkItem)
    dispatchWorkItems.append(itemsWorkItem)



    let queue = DispatchQueue(label: "com.dataLoader")
    let group = DispatchGroup()

    dispatchWorkItems.forEach{queue.async(group: group, execute: $0)}

    group.notify(queue: .main) {
        main{

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我如何简化上述方法,或者如何应用semaphores或任何其他可接受的方法来帮助我等待,直到我收到 a 的响应,DispatchworkItem然后再继续执行DispatchworkItem队列中的 下一个

从服务器获取数据的 modelView 如下所示

    func getInventory(completion: @escaping ArrayClosure<[InventoryClass], String>){
    let parameters :  [(String,AnyObject)] = [

        ("PageNumber" ,  "1" as AnyObject),
        ("Limit","1000" as AnyObject),
        ("BranchIds","\(business.branch?.id ?? "")" as AnyObject),
        ("canBeSold","true" as AnyObject)

    ]

    InventoryService(authorizationHeader:  self.header).getInventory(parameters: parameters) { request in
        switch request {

        case .success(let data):
            guard let finalData = data.data else  {return completion([], "Request to get Inventory Items was sucessfull but items count is 0")}
            return completion([finalData],"")
        case .failure(let error):
             return completion([],error.localizedDescription)

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 5

DispatchWorkItem只是不太适合管理本身异步的任务之间的依赖关系。从理论上讲,可以使用诸如信号量之类的笨拙技术来管理 中异步任务之间的依赖关系DispatchWorkItem,但这是一种反模式,应尽力避免。

\n

从历史上看,管理异步任务之间依赖关系的优雅解决方案是自定义异步Operation子类,但正如您将在下面的原始答案中看到的那样,它不必要地复杂。

\n

如今,异步任务之间依赖关系的首选解决方案asyncSwiftawait并发。请参阅 WWDC 2021 视频《在 Swift 中认识 async/await》。在该页面上,他们有大量指向其他并发相关视频的链接。一旦您理解了-的基本思想,Swift 并发:更新示例应用程序就是将旧版应用程序转换为 Swift 并发性的一个很好的实用演示。asyncawait

\n

因此,例如,使用async- await,您的问题的复杂性asyncCalls就被简化为非常琐碎的事情:

\n
func asyncCalls() async throws -> [InventoryClass] {\n    let categories = try await service.getCategories()\n    return try await service.getInventory(for: categories)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在此代码片段中,它将\xe2\x80\x99t 调用getInventory直到getCategories返回。它在不阻塞任何线程的情况下实现了这一点。

\n

现在,在您的示例中,getCategories\xe2\x80\x99t 不会对其结果执行任何操作,但在上面我假设您想让它们按顺序运行,因为您需要第一个请求的结果才能执行第二个请求。(否则,您应该同时运行它们,而不是顺序运行。)

\n

不用说,您现在需要转换getCategoriesgetInventory使用async- await,而不是使用完成处理程序。我们在问题中没有足够的实现,因此在这一点上过于具体,但希望在观看上述一些视频(尤其是引导您完成示例应用程序更新的视频)之后,您会更好地了解如何转换它们。

\n
\n

在下面的原始示例中,我概述了Operation用于顺序下载一堆资源的子类示例。有了 Swift 并发性,所有这些愚蠢的事情都消失了,并减少到几行代码:

\n
func download(urls: [URL]) async throws {\n    for url in urls {\n        let (data, response) = try await URLSession.shared.data(from: url)\n        // do something with `data` and `response` here\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

与复杂的相比Operation,您\xe2\x80\x99将真正体会到 Swift 并发的优雅。

\n

所以,虽然我不再推荐Operation模式,如下面我的原始答案中所述,但我会将其包含在内以供历史参考:

\n
\n

当我想在异步任务之间建立依赖关系时,我历来使用Operation而不是DispatchWorkItem. (诚​​然,在 iOS 13 及更高版本中,我们可能会考虑合并\xe2\x80\x99s Future / Promise,但目前操作是可行的方法。)操作被设计为比DispatchWorkItem. 所以你可以使用一个值为 1 的队列maxConcurrentOperationCount,如下所示:

\n
let networkQueue = OperationQueue()\nnetworkQueue.maxConcurrentOperationCount = 1\n\nlet completionOperation = BlockOperation {\n    print("all done")\n}\n\nfor url in urls {\n    let operation = NetworkOperation(url: url) { result in\n        switch result {\n        case .failure(let error):\n            \xe2\x80\xa6\n\n        case .success(let data):\n            \xe2\x80\xa6\n        }\n    }\n    completionOperation.addDependency(operation)\n    networkQueue.addOperation(operation)\n}\n\nOperationQueue.main.addOperation(completionOperation)\n
Run Code Online (Sandbox Code Playgroud)\n

或者,您可以使用更合理的方法maxConcurrentOperationCount,并且仅在需要此顺序行为的操作之间使用依赖关系:

\n
let networkQueue = OperationQueue()\nnetworkQueue.maxConcurrentOperationCount = 4\n\nlet completionOperation = BlockOperation {\n    print("all done")\n}\n\nvar previousOperation: Operation?\n\nfor url in urls {\n    let operation = NetworkOperation(url: url) { result in\n        switch result {\n        case .failure(let error):\n            \xe2\x80\xa6\n\n        case .success(let data):\n            \xe2\x80\xa6\n        }\n    }\n    if let previousOperation {\n        operation.addDependency(previousOperation)\n    }\n    completionOperation.addDependency(operation)\n    networkQueue.addOperation(operation)\n    previousOperation = operation\n}\n\nOperationQueue.main.addOperation(completionOperation)\n
Run Code Online (Sandbox Code Playgroud)\n

NetworkOperation看起来可能是这样的:

\n
class NetworkOperation: AsynchronousOperation {\n    typealias NetworkCompletion = (Result<Data, Error>) -> Void\n    \n    enum NetworkError: Error {\n        case invalidResponse(Data, URLResponse?)\n    }\n    \n    private var networkCompletion: NetworkCompletion?\n    private var task: URLSessionTask!\n    \n    init(request: URLRequest, completion: @escaping NetworkCompletion) {\n        super.init()\n        \n        task = URLSession.shared.dataTask(with: request) { data, response, error in\n            defer {\n                self.networkCompletion = nil\n                self.finish()\n            }\n            \n            guard let data = data, error == nil else {\n                self.networkCompletion?(.failure(error!))\n                return\n            }\n            \n            guard\n                let httpResponse = response as? HTTPURLResponse,\n                200..<300 ~= httpResponse.statusCode\n                else {\n                    self.networkCompletion?(.failure(NetworkError.invalidResponse(data, response)))\n                    return\n            }\n            \n            self.networkCompletion?(.success(data))\n        }\n        networkCompletion = completion\n    }\n    \n    convenience init(url: URL, completion: @escaping NetworkCompletion) {\n        self.init(request: URLRequest(url: url), completion: completion)\n    }\n    \n    override func main() {\n        task.resume()\n    }\n    \n    override func cancel() {\n        task.cancel()\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是回传Data,但是您可以编写排列/子类,将其进一步解析为您的 Web 服务返回的任何内容JSONDecoder或其他内容。但希望这能说明基本思想。

\n

上面使用了这个AsynchronousOperation类:

\n
let networkQueue = OperationQueue()\nnetworkQueue.maxConcurrentOperationCount = 1\n\nlet completionOperation = BlockOperation {\n    print("all done")\n}\n\nfor url in urls {\n    let operation = NetworkOperation(url: url) { result in\n        switch result {\n        case .failure(let error):\n            \xe2\x80\xa6\n\n        case .success(let data):\n            \xe2\x80\xa6\n        }\n    }\n    completionOperation.addDependency(operation)\n    networkQueue.addOperation(operation)\n}\n\nOperationQueue.main.addOperation(completionOperation)\n
Run Code Online (Sandbox Code Playgroud)\n

编写 base 的方法有很多AsynchronousOperation,我不想迷失在细节中,但我们的想法是,我们现在有了一个Operation可以用于任何异步进程的 。

\n