API 调用在循环中只返回一次响应

Posted

技术标签:

【中文标题】API 调用在循环中只返回一次响应【英文标题】:API call only returns response once in loop 【发布时间】:2016-12-15 18:05:36 【问题描述】:

我正在使用循环并对 CLGeocoder 进行 API 调用。 API 被调用了正确的次数,但是对于第一个 API 调用,completionHandler 只返回一次,然后它不会为其余的调用返回任何内容。

dispatch_async(dispatch_get_main_queue())
                                                
                        for shop in self.shopsList 
                            let shopAddress = shop.address
                            self.geocoder.geocodeAddressString(shopAddress, completionHandler:  (placemarks, error) in
                                if let error = error 
                                    print(error)
                                    return
                                
                                if let placemarks = placemarks where placemarks.count > 0 
                                    if let location = placemarks.first?.location 
                                        print("The longitude: \(location.coordinate.longitude)")
                                        print("The latitude: \(location.coordinate.latitude)")
                                    
                                
                            )
                        

                        self.spinner.dismiss()
                        self.categoryTableView.dataSource = self
                        self.categoryTableView.delegate = self
                        self.categoryTableView.reloadData()
                

其余的调用根本不返回任何内容,甚至不输入任何 if 语句。谁能告诉我我做错了什么?

我尝试在 for 循环的末尾添加sleep(2),但它仍然只返回一次

【问题讨论】:

【参考方案1】:

您不能像这样连续提交多个地理编码请求。除了第一个之外,所有的都可能被拒绝。

引用CLGeocoder上的文档:

应用程序应该意识到它们如何使用地理编码。地理编码 每个应用程序的请求都是有速率限制的,因此在 短时间内可能会导致部分请求失败。 (什么时候 超过最大速率,地理编码器返回一个错误对象 将值 kCLErrorNetwork 传递给关联的完成处理程序。) 以下是有效使用此类的一些经验法则:

最多为任何一项用户操作发送一个地理编码请求。

如果用户执行涉及对同一位置进行地理编码的多项操作,请重用初始地理编码请求的结果 而不是为每个操作启动单独的请求。

当您想自动更新用户的当前位置时(例如当用户移动时),仅发出新的地理编码请求 当用户移动了相当长的距离并经过合理的 时间已经过去了。例如,在典型情况下,您 每分钟不应发送超过一个地理编码请求。

不要在用户不会立即看到结果时启动地理编码请求。例如,如果出现以下情况,请不要启动请求 您的应用程序处于非活动状态或在后台。

您一次只能提交一个地理编码请求,并且每分钟提交的请求不得超过 ~1 个。这样的代码可以工作:

let arrayOfAddresses = ["123 main street denver CO", "100 W. 23rd street, NY, NY", ]

var addressesToGeocode: [String]!
var geocoder = CLGeocoder()  //Configure the geocoder as needed.

///Call this function to geocode your entire array of addresses.
func geocodeArrayOfaddresses() 
  //Copy over the array of addresses.
  addressesToGeocode = arrayOfAddresses
  geocodeNextAddress()


func geocodeNextAddress() 
  guard !addressesToGeocode.isEmpty else 
    return
  
  //Pull an address out of the array.
  let address = addressesToGeocode.removeLast()

  //Submit a geocoding request for this address.
  geocoder.geocodeAddressString(address) 
    (placemarks, error) in
    //Once we're done with the completion block, submit another geocoding
    //request after a minute delay.
    defer 
      //Wait 60 seconds before geocoding the next address.
      DispatchQueue.main.asyncAfter(wallDeadline: .now() + 60.0) 
        self.geocodeNextAddress()
      
    
    if let error = error 
      print(error)
    
    if let placemarks = placemarks,
      placemarks.count > 0 
      if let location = placemarks.first?.location 
        print("The longitude: \(location.coordinate.longitude)")
        print("The latitude: \(location.coordinate.latitude)")
      
    
  

【讨论】:

我已经尝试在for循环末尾添加sleep(2),但它仍然只返回一次 使用睡眠几乎从来都不是一个好主意,尤其是在主线程中。 rmaddy 在他的回答中包含了一个关于如何执行多个地理编码请求的建议的链接。至少,您需要重构代码以提交请求并等待其完成,然后再提交下一个请求。 使用 sleep(2) 是该建议链接中的建议 查看我的答案的编辑。我向您展示了如何通过延迟提交一个又一个请求。文档说您每分钟不应该提交超过一个地理编码请求,所以这就是我的示例代码所做的。【参考方案2】:

您应该阅读geocodeAddressString 方法的文档(突出显示我的):

该方法将指定的位置数据异步提交给地理编码服务器并返回。您的完成处理程序块将在主线程上执行。 发起正向地理编码请求后,不要尝试发起另一个正向或反向地理编码请求。

每个应用的地理编码请求都有速率限制,因此在短时间内发出过多请求可能会导致某些请求失败。当超过最大速率时,地理编码器将带有值网络的错误对象传递给您的完成处理程序。

所以基本上你不能使用一个简单的循环来发出一大堆并发请求。请参阅Strategy to perform many Geocode requests 了解一种可能的解决方案。

【讨论】:

我已经尝试在for循环末尾添加sleep(2),但它仍然只返回一次【参考方案3】:

为了避免这个问题,我在循环中声明了地理编码器:

...
for shop in self.shopsList 
    let shopAddress = shop.address
    **let geocoder = CLGeocoder()**
    **geocoder**.geocodeAddressString(shopAddress, completionHandler:  (placemarks, error) in
        if let error = error 
        print(error)
        return
        ...

【讨论】:

以上是关于API 调用在循环中只返回一次响应的主要内容,如果未能解决你的问题,请参考以下文章

编写Twisted Client以向多个API调用发送循环GET请求并记录响应

想在 uitablelview 中只返回 1 个单元格,但由于 json 响应,显示了多个单元格

循环等待 API 响应

为啥我的 For 循环在 EJS 节点 js 中只返回一个值?

如何在 DOM 上显示由 API 调用返回的 HTML 响应?

将多个API响应合并到一个JSON对象C#中