bpl*_*urg 7 nsoperationqueue grand-central-dispatch async-await swift
使用 Swift 的新 async/await 功能,我想模拟串行队列的调度行为(类似于过去使用 aDispatchQueue或 的方式)。OperationQueue
稍微简化一下我的用例,我有一系列异步任务,我想从调用站点触发并在完成时获得回调,但根据设计,我想一次只执行一个任务(每个任务取决于之前的任务完成)。
如今,这是通过将Operations放置到OperationQueue带有 a的 上maxConcurrentOperationCount = 1,以及Operation在适当的时候使用 的依赖功能来实现的。我已经使用现有的基于闭包的入口点构建了一个异步/等待包装器,await withCheckedContinuation但我正在尝试找出如何将整个方法迁移到新系统。
那可能吗?它是否有意义,或者我从根本上违背了新的异步/等待并发系统的意图?
我已经深入研究了 using Actors 但据我所知,没有办法用这种方法真正强制/期望串行执行。
--
更多上下文 - 这包含在一个网络库中,其中今天的每个操作都是针对一个新请求。该操作会执行一些请求预处理(如果适用,请考虑身份验证/令牌刷新),然后触发请求并继续执行下一个操作,从而避免在不需要时重复进行身份验证预处理。从技术上讲,每个操作并不知道它依赖于先前的操作,但操作队列的调度强制串行执行。
添加下面的示例代码:
// Old entry point
func execute(request: CustomRequestType, completion: ((Result<CustomResponseType, Error>) -> Void)? = nil) {
let operation = BlockOperation() {
// do preprocessing and ultimately generate a URLRequest
// We have a URLSession instance reference in this context called session
let dataTask = session.dataTask(with: urlRequest) { data, urlResponse, error in
completion?(/* Call to a function which processes the response and creates the Result type */)
dataTask.resume()
}
// queue is an OperationQueue with maxConcurrentOperationCount = 1 defined elsewhere
queue.addOperation(operation)
}
// New entry point which currently just wraps the old entry point
func execute(request: CustomRequestType) async -> Result<CustomResponseType, Error> {
await withCheckedContinuation { continuation in
execute(request: request) { (result: Result<CustomResponseType, Error>) in
continuation.resume(returning: result)
}
}
}
Run Code Online (Sandbox Code Playgroud)
一些观察:
\n为了清楚起见,您的操作队列实现不会\ xe2\x80\x9c[强制]网络请求的串行执行\xe2\x80\x9d。您的操作仅包装这些请求的准备,而不包装这些请求的执行(即操作立即完成,而不等待请求完成)。因此,例如,如果您的身份验证是一个网络请求,而第二个请求要求在继续之前完成该身份验证,则这种BlockOperation实现不是正确的解决方案。
一般来说,如果使用操作队列来管理网络请求,您可以将整个网络请求和响应包装在自定义的异步Operation子类(而不是 )中BlockOperation,此时您可以使用操作队列依赖项和/或maxConcurrentOperationCount. 如果您想了解包装网络请求的子类是什么样子,请参阅/sf/answers/4007350861/ 。Operation但它没有实际意义,因为你现在应该使用async它await。
你说:
\n\n\n我基本上可以完全跳过队列,并
\nOperation用演员上的异步方法替换每个队列来完成同样的事情?
不会。Actor 可以确保同步方法的顺序执行(那些没有await调用的方法,在这些情况下,您不希望async方法本身有限定符)。
但如果你的方法确实是异步的,那么,不,参与者将无法确保顺序执行。Actor 是为可重入而设计的。请参阅SE-0306 - Actors \xc2\xbb Actor 可重入性。
\n如果您希望后续的网络请求等待身份验证请求完成,您可以保存Task身份验证请求的内容。然后后续请求可以执行await该任务:
actor NetworkManager {\n let session: URLSession = ...\n\n var loginTask: Task<Bool, Error>?\n\n func login() async throws -> Bool {\n loginTask = Task { () -> Bool in\n let _ = try await loginNetworkRequest()\n return true\n }\n return try await loginTask!.value\n }\n\n func someOtherRequest(with value: String) async throws -> Foo {\n let isLoggedIn = try await loginTask?.value ?? false\n guard isLoggedIn else {\n throw URLError(.userAuthenticationRequired)\n }\n return try await foo(for: createRequest(with: value))\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n如果您正在寻找一般的类似队列的行为,您可以考虑AsyncChannel. 例如,在/sf/answers/5301133841/中,我创建了一个AsyncChannelfor URL,编写了一个迭代该通道的循环,并为每个通道执行下载。然后,当我想要开始新的下载时,我创建了send该频道的新 URL。
也许这是不相关的,但如果您要引入 async-await,我建议不要使用withCheckedContinuation. 显然,如果 iOS 15(或 macOS 12)及更高版本,我将使用新的异步URLSession方法。例如,如果您需要返回到 iOS 13,我会使用withTaskCancellationHandler和withThrowingCheckedContinuation。请参阅/sf/answers/4929141801/。
| 归档时间: |
|
| 查看次数: |
4404 次 |
| 最近记录: |