我试图组织我的登录过程与swifts DispatchQueue()和completionHandlers。首先,用户应该登录并从服务器接收jwt -令牌,然后应该自动下载一些东西。
我确实有一个名为httpRequest(requestType:HTTPRequestType, parameter:Parameter = Parameter(), completionHandler: @escaping ((Result<Parameter?, HTTPRequestError>) -> Void) = { _ in })
的函数来处理我所有的http请求。如果我的请求类型.login
完成HTTP 200,它执行(在httpRequest
内)
DispatchQueue.main.async {
let jsonObject = try? decodeParameter(data: data)
self.httpToken = jsonObject?.httpToken
self.sessionName = parameter.sessionName!
}
在httpRequest()
的.success()
上,我的数据的下载应该开始,不幸的是,有时确实会发生,下载开始得太早-在该令牌存储之前。这意味着没有身份验证,这使得服务器拒绝我的请求。我确实尝试了queue = DispatchQueue("com.my.label", qos: .utility)
(应该是串行的)并添加了
queue.async {
// login
httpRequest(requestType: .login) // with parameter, with or without completionHandler
}
queue.async {
// download
httpRequest(requestType: .download)
}
但是这也给了我相同的行为
与queue = OperationQueue()
和queue.maxConcurrentOperationCount = 1
相同,显然queue.addOperation {}
任何提示如何解决这个问题,并确保,登录完成,然后下载开始?以后还会有一些相互依赖的下载,这只是第一次失败。
httpRequest
是一个异步方法,这意味着它启动请求,然后立即返回。因此,您的各种调度/操作队列方法将使问题变得更糟,因为您在初始化登录请求后立即开始下载,而不是等待登录请求完成。
必须使用完成处理程序:
httpRequest(requestType: .login) { result in
switch result {
case .failure:
// handle error here
case .success:
httpRequest(requestType: .download)
}
}
这种模式的优点是你以不同的方式处理成功和失败。例如,如果登录失败,是否有必要尝试所有下载?
那么,让我们假设您正确地使用了完成处理程序模式,如上所述。如果问题仍然存在,那么问题可能与设置httpToken
时相关的竞争有关。例如:
值得注意的是,这里没有调用完成处理程序。如果异步调度令牌和会话变量的更新,但立即调用完成处理程序,则可以在设置这些会话属性之前调用完成处理程序。如果我的
requestType
.login
完成HTTP 200,它执行(在httpRequest
内)DispatchQueue.main.async { let jsonObject = try? decodeParameter(data: data) self.httpToken = jsonObject?.httpToken self.sessionName = parameter.sessionName! }
因此,您可能希望在设置会话属性之后,将此调度中的httpRequest
完成处理程序的调用移动到主队列。
这并不重要,但是有完成处理程序模式的替代方案。其他方法包括将httpRequest
包装在自定义的异步Operation
子类中,该子类仅在请求完成时设置isFinished
。或结合。或者使用Swift 5.5引入的async/await模式。还有其他选择。但是,最重要的是,您希望采用异步模式,该模式在请求完成时起作用,而不仅仅是在请求发起时起作用。