swift - 你能有同步但非阻塞的 URLSessions 吗?
问题描述
我正在使用本质上是异步的 URLSessions。当会话只有一个呼叫时,这很有效。当我需要执行多个(串行)调用时,需要按执行顺序组合结果,这会使程序逻辑痛苦且容易出错。我也阻塞了主线程,这不好。
约束
移动到下一个任务可能不会在 4 秒过去之前发生,但可能会更长。
利用 Monterey(需要操作系统升级),因此使用 let (data, response) = try await session.data(from: url) 不是可选的。
如果任务的执行速度快于每 4 秒,则结果是服务器端错误,强制重试。
任务执行
- 执行任务——如果任务在 4 秒内结束,则等待差异,以便在 4 秒过去之前不执行下一个任务。
- 重复这个过程,直到所有任务都完成。
- 合并结果
我当前的进程使用信号量或 dispatchGroups 但都阻塞了主线程。
有没有办法在不阻塞主线程的情况下获得同步行为?
func getDataFromInput_Sync(authToken: String, transformedText: String ) -> (Data?, URLResponse?, Error?)
{
/*
By default, transactions are asynchronous, so they return data while the rest of the program continues.
Changing the behavior to synchronous requires blocking to wait for the outcome. It affords us the
ability to manage program flow inline. This methods forces synchronous behavior and the output, which
are returned via a tuple, types: (Data?, URLResponse?, Error?)
*/
var outData : Data?
var outError : Error?
var urlResponse : URLResponse?
let targetURL = "https://..."
let urlconfig = URLSessionConfiguration.ephemeral
// set the timeout to a high number, if we are prepared to wait that long. Otherwise the session will timeout.
urlconfig.timeoutIntervalForRequest = 120
urlconfig.timeoutIntervalForResource = 120
let urlSession = URLSession(configuration: urlconfig)
// let dispatchGroup = DispatchGroup()
let semaphore = DispatchSemaphore(value: 0)
// ephermeral doesnt write cookies, cache or credentials to disk
guard let url = URL(string: targetURL),
let httpBodyData = transformedText.data(using: .utf8) else { return (nil,nil,nil) }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = httpBodyData
request.addValue("Token " + authToken, forHTTPHeaderField: "Authorization")
// Perform HTTP Request
let task = (urlSession).dataTask(with: request) { (data, response, error) in
guard error == nil
else {
print("we have an error: \(error!.localizedDescription)")
return
}
guard let data = data else { print("Empty data"); return }
outData = data
urlResponse = response
outError = error
// dispatchGroup.leave()
semaphore.signal()
}
task.resume()
semaphore.wait()
// dispatchGroup.enter()
// task.resume()
// dispatchGroup.wait()
return (outData, urlResponse, outError)
}
func testServerRequest()
{
let sentences = ["Example Sentence1","Example Sentence2","Example Sentence3"] //...array to process
for (_,thisString) in sentences.enumerated()
{
let timeTestPoint = Date()
let futureSecs = 4.0
let (data, urlResponse, error) = getDataFromInput_Sync(authToken: authToken, transformedText: thisString )
let elapsed = timeTestPoint.timeIntervalSinceNow * -1 // time elapsed between request and result
// act on the data received
// executing the next request before futureSecs will cause an error, so pause
let delayAmt = futureSecs - elapsed
Thread.sleep(forTimeInterval: delayAmt)
}
}
解决方案
让它在后台队列中,比如
DispatchQueue.global(qos: .background).async {
testServerRequest()
}
推荐阅读
- xamarin.forms - 使用 Observable 进行 CollectionView 分组
- apache-spark - Spark SQL 中的迭代广播联接
- sql - 执行时间:为什么相同代码和相同参数的持续时间会有很大差异?
- mysql - 在mysql用户定义函数中使用if else
- javascript - 如何比较两个数组是否有重复值并返回不同的值?
- python - Kivy python回调在android设备上不起作用
- java - 将位转换为 Base64 字符
- mysql - MySQL 错误代码 #1064 在插入前创建触发器时靠近 IF ELSE 块
- javascript - 如何在javascript中的onclick后延迟重定向?
- c++ - 获取多维可变参数 std::array 的根类型