【问题标题】:Multi pages API slows down loading多页 API 减慢加载速度
【发布时间】:2021-05-13 06:52:04
【问题描述】:

我正在使用这个FREE NBA API 进行技术测试。加载团队既简单又简单。问题是我必须显示特定球队的所有球员。球队不返回球员,只返回球队信息:

"data":[
0:{
"id":1
"abbreviation":"ATL"
"city":"Atlanta"
"conference":"East"
"division":"Southeast"
"full_name":"Atlanta Hawks"
"name":"Hawks"
}
1:{...}
2:{...}
]

所以我必须获取在最多 100 个结果的几页(大约 35 个)中返回的所有玩家。我想出了这个有效的代码,但有时需要更长的时间,我想尝试其他的。

func loadPlayers(team:Team, pages: Int?, completion: @escaping (_ error:Error?, _ players:[Player]) -> ()){
        var players = [Player]()
        let rest = RestManager()
        for (key, value) in headers {
            rest.requestHttpHeaders.add(value: value, forKey: key)
        }
        //loop thought the API pages this is o(n) but the API is really badly designed as teams doesen't have an array with the players
        for page in 1..<pages! {
            rest.makeRequest(withEndPoint: "players?per_page=100&page=\(page)", withHttpMethod: .get) { (result) in
                guard let response = result.response else {return}
                if response.httpStatusCode == 200 {
                    guard let data = result.data else {return}
                    print(data.printJSON())
                    let decoder = JSONDecoder()
                    guard let playerResult = try? decoder.decode(PlayerData.self, from: data) else { return }
                    for player in playerResult.data {
                        DispatchQueue.main.async {
                            if player.team?.abbreviation == team.abbreviation {
                                players.append(player)
                            }
                        }
                    }
                }else{
                    guard let error = result.error else {return}
                    completion(error, [])
                }
                completion(nil, players)
            }
        }
    }

这是我的数据模型:

struct PlayerData : Decodable {
    var data:[Player]
    var meta:MetaDataPlayer
}
struct Player : Decodable {
    var id:Int?
    var first_name:String?
    var last_name:String?
    var height_feet:Int?
    var height_inches:Int?
    var weight_pounds:Int?
    var position:String?
    var team:Team?
}

struct MetaDataPlayer : Decodable {
    var total_pages:Int? // needed to loop through all pages of the API
    var current_page:Int?
    var next_page:Int?
    var per_page:Int?
    var total_count:Int?
}

【问题讨论】:

    标签: ios json swift api


    【解决方案1】:

    在尝试使用大型分页数据集时,您可以采用几种不同的方法。可能会看到最大的性能改进的两个是:

    并行调用 API。

    您的每个调用都依赖于等待上一个调用完成。随着页数的增加,您的总时间也会增加。一个呼叫中的错误将延迟以后的每个呼叫。通过将调用拆分为并行调用,您可以消除此瓶颈。您可能会遇到速率限制问题,因此您可以进行多个查询的数量和速度需要进行一些试验。

    在需要之前预取页面。

    当完整数据的子集可以从其他大型数据集中使用时,通常会使用分页结果。如果您的特定用途需要完整数据集才能对其执行操作,请考虑在用户需要之前在后台更早的时间启动获取过程。这样一来,瓶颈 API 调用就会在用户需要的时候完成。

    【讨论】:

      【解决方案2】:

      我最终使用了DispatchSemaphore,它使我们能够控制多个线程对共享资源的访问:

       func loadPlayersWithConcurrentCalls(team:Team, completion: @escaping (_ error:Error?, _ players:[Player]) -> ()){
              //Create a dispatch queue
              var players = [Player]()
              var playerData:PlayerData?
              self.loadPlayersData { (error, data) in
                  if error != nil{
                      print(error.debugDescription)
                  }else{
                      guard let data = data else {return}
                      playerData = data
                  }
                  let rest = RestManager()
                  for (key, value) in self.headers {
                      rest.requestHttpHeaders.add(value: value, forKey: key)
                  }
                  //added semaphore and backgroundQueue to manage multiple api calls
                  let backgroundQueue = DispatchQueue(label: "requests")
                  let semaphore = DispatchSemaphore(value: 1)
                  backgroundQueue.async {
                      for page in 1..<playerData!.meta.total_pages! {
                          semaphore.wait()
                          rest.makeRequest(withEndPoint: "players?per_page=100&page=\(page)", withHttpMethod: .get) { (result) in
                              guard let response = result.response else {return}
                              if response.httpStatusCode == 200 {
                                  guard let data = result.data else {return}
                                  let decoder = JSONDecoder()
                                  guard let playerResult = try? decoder.decode(PlayerData.self, from: data) else { return }
                                  players.append(contentsOf: playerResult.data)
                                      semaphore.signal()
                              }else{
                                  print(response.httpStatusCode)
                                  completion(error,[])
                              }
                              completion(nil,players)
                          }
                      }
                  }
              }
          }
      

      我真的很喜欢这个结果:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-06
        • 2022-01-13
        • 2019-05-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多