首页 文章

Swift 4:如何异步使用URLSessionDataTask但请求是否在一个定时队列中?

提问于
浏览
2

基本上我有一些JSON数据,我希望从一堆URL(所有来自同一主机)中检索,但是我只能每2秒钟大致请求这些数据,一次只能请求一个,或者我将是“时间”禁止“从服务器 . 正如你在下面看到的那样;虽然URLSession非常快,但当我有大约700个网址可以通过时,它几乎可以立即被禁止 .

我将如何在URLSession中创建一个队列(如果它的功能支持它)并且让它与我的主线程异步工作;让它在自己的线程上连续工作,并且只有在完成上一个请求2秒后才尝试队列中的每个项目?

for url in urls {
    get(url: url)
}


func get(url: URL) {
    let session = URLSession.shared
    let task = session.dataTask(with: url, completionHandler: { (data, response, error) in

        if let error = error {
            DispatchQueue.main.async {
                print(error.localizedDescription)
            }
            return
        }
        let data = data!

        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            DispatchQueue.main.async {
                print("Server Error")
            }
            return
        }
        if response.mimeType == "application/json" {
            do {
                let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
                if json["success"] as! Bool == true {
                    if let count = json["total_count"] as? Int {
                        DispatchQueue.main.async {
                            self.itemsCount.append(count)
                        }
                    }
                }
            } catch {
                print(error.localizedDescription)
            }
        }
    })
    task.resume()
}

3 回答

  • 1

    递归解决了这个问题

    import Foundation
    import PlaygroundSupport
    
    // Let asynchronous code run
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    func fetch(urls: [URL]) {
    
        guard urls.count > 0 else {
            print("Queue finished")
            return
        }
    
        var pendingURLs = urls
        let currentUrl = pendingURLs.removeFirst()
    
        print("\(pendingURLs.count)")
    
        let session = URLSession.shared
        let task = session.dataTask(with: currentUrl, completionHandler: { (data, response, error) in
            print("task completed")
            if let _ = error {
                print("error received")
                DispatchQueue.main.async {
                    fetch(urls: pendingURLs)
                }
                return
            }
    
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                print("server error received")
                DispatchQueue.main.async {
                    fetch(urls: pendingURLs)
                }
                return
            }
            if response.mimeType == "application/json" {
                print("json data parsed")
                DispatchQueue.main.async {
                    fetch(urls: pendingURLs)
                }
            }else {
                print("unknown data")
                DispatchQueue.main.async {
                    fetch(urls: pendingURLs)
                }
            }
        })
    
        //start execution after two seconds
        Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
            print("resume called")
            task.resume()
        }
    }
    
    var urls = [URL]()
    for _ in 0..<100 {
        if let url = URL(string: "https://google.com") {
            urls.append(url)
        }
    }
    
    fetch(urls:urls)
    
  • 0

    最简单的方法是执行递归调用:

    • 想象一下,你的网址中有数组 .

    • 在最初执行循环的位置,将其替换为单个调用 get(url:) . self.get(urls[0])

    • 然后在 self.itemsCount.append(count) 之后立即在响应闭包处添加此行:

    self.urls.removeFirst()
    Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (_) in
    self.get(url: urls[0])
    }
    
  • 0

    使 DispatchQueue 在线程上运行代码 . 您不需要在主线程上执行此操作 . 所以,

    // make serial queue
    let queue = DispatchQueue(label: "getData")
    
    // for delay
    func wait(seconds: Double, completion: @escaping () -> Void) {
        queue.asyncAfter(deadline: .now() + seconds) { completion() }
    }
    
    // usage
    for url in urls {
      wait(seconds: 2.0) {
          self.get(url: url) { (itemCount) in
             // update UI related to itemCount
          }
      }
    }
    

    顺便说一下,你的 get(url: url) 功能并不是那么好 .

    func get(url: URL, completionHandler: @escaping ([Int]) -> Void) {
        let session = URLSession.shared
        let task = session.dataTask(with: url, completionHandler: { (data, response, error) in
    
            if let error = error {
                print(error.localizedDescription)
                /* Don't need to use main thread
                    DispatchQueue.main.async {
                        print(error.localizedDescription)
                    }
                */
                return
            }
            let data = data!
    
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                print("Server Error")
                /* Don't need to use main thread
                    DispatchQueue.main.async {
                        print("Server Error")
                    }
                */
                return
            }
            if response.mimeType == "application/json" {
                do {
                    let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
                    if json["success"] as! Bool == true {
                        if let count = json["total_count"] as? Int {
    
                            self.itemsCount.append(count)
    
                            // append all data that you need and pass it to completion closure
                            DispatchQueue.main.async {
                                completionHandler(self.itemsCount)
                            }
                        }
                    }
                } catch {
                    print(error.localizedDescription)
                }
            }
        })
        task.resume()
    }
    

    我建议你学习 GCD (用于线程)和 escaping closure (用于完成处理程序)的概念 .

相关问题