如何同步 URLSession 任务的串行队列?
Posted
技术标签:
【中文标题】如何同步 URLSession 任务的串行队列?【英文标题】:How to sync serial queue for URLSession tasks? 【发布时间】:2017-06-22 17:17:49 【问题描述】:使用 XCode-8.2.1、Swift-3.0.2 和 ios-10.2.1,
我正在尝试调用两个不同的 URLSession.shared.dataTasks(第一个是简单的 URL 请求,第二个是 POST 请求)。
由于我的第一个 dataTask 传递了第二个 dataTask 的 httpBody 所需的结果,因此两个 URLSession.shared.dataTask 应该一个接一个地连续运行! (准备代码也要连续运行)。
到目前为止,我尝试使用两个连续的serialQueue.sync
队列。但我不得不意识到代码并没有按照我想要的顺序执行。
日志中的打印语句如下:
Hmmmmmm 2
Hmmmmmm 1
Hmmmmmm 3
(根据需要代替 1、2、3)!
1、2、3的顺序怎么得到??
(即如何确保第二个 dataTask 的 httpBody 可以填充来自第一个 dataTask 的结果?)
这是我的代码:(不可执行,因为 URL 已被删除 - 但你明白了)!
import UIKit
class ViewController: UIViewController
let serialQueue = DispatchQueue(label: "myResourceQueue")
var stationID: Int = 0
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.serialQueue.sync
let myResourceURL = URL(string: "myQueryString1")
let task = URLSession.shared.dataTask(with: myResourceURL!) (data, response, error) in
if (error != nil)
// print(error.debugDescription)
else
if let myData = data
do
let myJson = try JSONSerialization.jsonObject(with: myData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
// print(myJson)
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print("Hmmmmmm 1")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
catch
// error
task.resume()
self.serialQueue.sync
var request = URLRequest(url: URL(string: "myQueryString2")!)
request.httpMethod = "POST"
request.addValue("API_id", forHTTPHeaderField: "Authorization")
request.addValue("application/xml", forHTTPHeaderField: "Content-Type")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print("Hmmmmmm 2")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let postString: String = "My_XML_POST_Body"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) data, response, error in
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print("Hmmmmmm 3")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
task.resume()
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
任何帮助表示赞赏!
【问题讨论】:
搞笑 - 我想在 19 小时后知道同样的事情! :) 你说得对,URLSession.shared.dataTask 好像跳出了serialQueue。 另一种方法是使用(a)具有maxConcurrentOperationCount
或1
的操作队列;或 (b) 您在操作之间建立依赖关系。但要使其正常工作,您需要执行异步 Operation
/NSOperation
子类,例如 ***.com/questions/32322386/…。
【参考方案1】:
我终于找到了解决办法:
受this answer 的启发,我介绍了URLSessionDataDelegate
及其委托回调方法(即didReceive response:
、didReceive data:
和didCompleteWithError error:
。
重要提示:您需要使用委托设置 URLSession,以使引入的 URLSessionDelegate 的回调方法起作用:使用 URLSession(configuration: ....) 如下所示:
let URLSessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main)
之后,您就可以开始了,即日志现在符合预期:
Hmmmmmm 1
Hmmmmmm 2
Hmmmmmm 3
这是最终代码(同样不可执行,因为 URL 已被删除 - 但你明白了)!
import UIKit
class ViewController: UIViewController, URLSessionDataDelegate
var stationID: Int = 0
let URLSessionConfig = URLSessionConfiguration.default
var session: URLSession?
var task1: URLSessionTask?
var task2: URLSessionTask?
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.session = URLSession(configuration: URLSessionConfig, delegate: self, delegateQueue: OperationQueue.main)
// prepare dataTask Nr 1
let myResourceURL = URL(string: "myQueryString1")
self.task1 = session?.dataTask(with: myResourceURL!)
// start dataTask Nr 1 (URL-request)
self.task1?.resume()
// Optional: Use this method if you want to get a response-size information
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
// print(Int(response.expectedContentLength))
completionHandler(URLSession.ResponseDisposition.allow)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
if dataTask == self.task1
do
let myJson = try JSONSerialization.jsonObject(with: myData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
// print(myJson)
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print("Hmmmmmm 1")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// prepare dataTask Nr 2
self.task2 = self.session?.dataTask(with: self.prepareMyURLRequest())
catch
// error
else if dataTask == self.task2
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print("Hmmmmmm 3")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
else
print("unknown dataTask callback")
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
if (error != nil)
// print(error.debugDescription)
else if task == self.task1
// start dataTask Nr 2 (POST URL-request)
self.task2?.resume()
func prepareMyURLRequest() -> URLRequest
var request = URLRequest(url: URL(string: "myQueryString2")!)
request.httpMethod = "POST"
request.addValue("API_id", forHTTPHeaderField: "Authorization")
request.addValue("application/xml", forHTTPHeaderField: "Content-Type")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print("Hmmmmmm 2")
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
let postString: String = "My_XML_POST_Body"
request.httpBody = postString.data(using: .utf8)
return request
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
【讨论】:
iKK - 但是,您只是按顺序调用它们。正确的?你没有使用serialQueue,我不认为?? iKK - 这很好。但显然,您不必通过基于委托的实现来完成所有这些工作。您可以使用更简单的完成处理程序模式完成完全相同的事情。 谢谢罗伯。你手头有这样的例子吗?【参考方案2】:如果task2需要task1的结果,你应该从task1的完成块开始task2
URLSession.shared.dataTask(with: request) data, response, error in
// process task1 and setup request2
URLSession.shared.dataTask(with: request2) data, response, error in
// process task2
.resume()
.resume()
当然,这会因为多个请求而变得有点笨拙,所以使用 Promises & Futures 可能会更好。 Swift 的 Promises & Futures 有几种实现,例如Promis.
【讨论】:
以上是关于如何同步 URLSession 任务的串行队列?的主要内容,如果未能解决你的问题,请参考以下文章
同步,异步,串行队列,并发队列,全局队列,主队列等概念的总结