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)
DispatchWorkItem只是不太适合管理本身异步的任务之间的依赖关系。从理论上讲,可以使用诸如信号量之类的笨拙技术来管理 中异步任务之间的依赖关系DispatchWorkItem,但这是一种反模式,应尽力避免。
从历史上看,管理异步任务之间依赖关系的优雅解决方案是自定义异步Operation子类,但正如您将在下面的原始答案中看到的那样,它不必要地复杂。
如今,异步任务之间依赖关系的首选解决方案async是Swiftawait并发。请参阅 WWDC 2021 视频《在 Swift 中认识 async/await》。在该页面上,他们有大量指向其他并发相关视频的链接。一旦您理解了-的基本思想,Swift 并发:更新示例应用程序就是将旧版应用程序转换为 Swift 并发性的一个很好的实用演示。asyncawait
因此,例如,使用async- await,您的问题的复杂性asyncCalls就被简化为非常琐碎的事情:
func asyncCalls() async throws -> [InventoryClass] {\n let categories = try await service.getCategories()\n return try await service.getInventory(for: categories)\n}\nRun Code Online (Sandbox Code Playgroud)\n在此代码片段中,它将\xe2\x80\x99t 调用getInventory直到getCategories返回。它在不阻塞任何线程的情况下实现了这一点。
现在,在您的示例中,getCategories\xe2\x80\x99t 不会对其结果执行任何操作,但在上面我假设您想让它们按顺序运行,因为您需要第一个请求的结果才能执行第二个请求。(否则,您应该同时运行它们,而不是顺序运行。)
不用说,您现在需要转换getCategories并getInventory使用async- await,而不是使用完成处理程序。我们在问题中没有足够的实现,因此在这一点上过于具体,但希望在观看上述一些视频(尤其是引导您完成示例应用程序更新的视频)之后,您会更好地了解如何转换它们。
在下面的原始示例中,我概述了Operation用于顺序下载一堆资源的子类示例。有了 Swift 并发性,所有这些愚蠢的事情都消失了,并减少到几行代码:
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}\nRun Code Online (Sandbox Code Playgroud)\n与复杂的相比Operation,您\xe2\x80\x99将真正体会到 Swift 并发的优雅。
所以,虽然我不再推荐Operation模式,如下面我的原始答案中所述,但我会将其包含在内以供历史参考:
当我想在异步任务之间建立依赖关系时,我历来使用Operation而不是DispatchWorkItem. (诚然,在 iOS 13 及更高版本中,我们可能会考虑合并\xe2\x80\x99s Future / Promise,但目前操作是可行的方法。)操作被设计为比DispatchWorkItem. 所以你可以使用一个值为 1 的队列maxConcurrentOperationCount,如下所示:
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)\nRun Code Online (Sandbox Code Playgroud)\n或者,您可以使用更合理的方法maxConcurrentOperationCount,并且仅在需要此顺序行为的操作之间使用依赖关系:
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)\nRun Code Online (Sandbox Code Playgroud)\nNetworkOperation看起来可能是这样的:
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}\nRun Code Online (Sandbox Code Playgroud)\n这是回传Data,但是您可以编写排列/子类,将其进一步解析为您的 Web 服务返回的任何内容JSONDecoder或其他内容。但希望这能说明基本思想。
上面使用了这个AsynchronousOperation类:
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)\nRun Code Online (Sandbox Code Playgroud)\n编写 base 的方法有很多AsynchronousOperation,我不想迷失在细节中,但我们的想法是,我们现在有了一个Operation可以用于任何异步进程的 。
| 归档时间: |
|
| 查看次数: |
2014 次 |
| 最近记录: |