你能有同步但非阻塞的 URLSessions 吗?
Can you have synchronous but non-blocking URLSesssions?
我正在使用本质上异步的 URLSession。
当只有一个会话调用时,此方法很有效。
当我需要执行多个(串行)调用时,需要按执行顺序组合结果,这会使程序逻辑变得痛苦且容易出错。
我也阻塞了主线程,这不好。
约束条件
在 4 秒过去之前可能不会移动到下一个任务,尽管它可能会更长。
利用蒙特雷(OS 需要升级),所以使用 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()
}
我正在使用本质上异步的 URLSession。 当只有一个会话调用时,此方法很有效。 当我需要执行多个(串行)调用时,需要按执行顺序组合结果,这会使程序逻辑变得痛苦且容易出错。 我也阻塞了主线程,这不好。
约束条件
在 4 秒过去之前可能不会移动到下一个任务,尽管它可能会更长。
利用蒙特雷(OS 需要升级),所以使用 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()
}