2017-12-07 16 views
1

基本的に私はいくつかのJSONデータを(同じホストからの)複数のURLから取得したいと考えていますが、このデータは最低でも2秒ごとにしか要求できません。サーバーから「禁止されている時間」。あなたが下記で見るように。 URLSessionは非常に速いですが、私は約700個のURLを持っていれば、ほとんど即座に禁止されています。Swift 4:URLSessionDataTaskを非同期的に使用する方法はありますが、要求はタイム・キューに入れられますか?

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() 
} 

答えて

-1

最も簡単な方法は、再帰呼び出しを実行することです:

  1. はあなたのURLを持つ配列を持っている想像してみてください。

  2. ループで最初に実行した場所では、単一コールget(url:)に置き換えてください。 self.get(urls[0])

  3. 次に右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

+0

を解決しますが、低速の接続になり、サーバーの呼び出しの間の実際の時間は変更になる場合があります場合はどう、 'get'がすぐに戻るからです。 –

+0

@DenisLitvinその場合、要求の一部が失敗し、エラーメッセージが出力されます。それを処理したい場合は、urlsessionデータタスクからタイムアウト設定を行う必要があります。 –

+0

いいえ、そのIPはサーバーの制限により禁止されます。これは質問の中で述べたように –

0

再帰が、このベスト

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) 
関連する問題