你能有同步但非阻塞的 URLSessions 吗?

Can you have synchronous but non-blocking URLSesssions?

我正在使用本质上异步的 URLSession。 当只有一个会话调用时,此方法很有效。 当我需要执行多个(串行)调用时,需要按执行顺序组合结果,这会使程序逻辑变得痛苦且容易出错。 我也阻塞了主线程,这不好。

约束条件

在 4 秒过去之前可能不会移动到下一个任务,尽管它可能会更长。

利用蒙特雷(OS 需要升级),所以使用 let (data, response) = try await session.data(from: url) 不是可选的。

如果任务执行速度快于每 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()
}