Swift:递归异步函数,带有不被调用的完成处理程序



我需要以异步方式从端点获取大量数据。API 终端节点一次提供预定义的数据量。在第一个请求之后,我必须检查我是否从响应中获得"下一个"URL,并访问该链接以继续下载。这种递归行为一直持续到提供所有可用数据为止,换句话说,分页功能(HAL 链接(。在这一点上,我已经实现了一个递归下载的func,但是:问题是最终完成处理程序似乎没有被调用。

演示代码:ThingsApi 是一个封装实际 API 调用的类。重要的是,这个类有一个初始 url,在递归期间将获取特定的 url 以异步访问。我调用downloadThings()函数,完成后需要收到通知。如果我将递归排除在等式之外,它会起作用。但是当递归发挥作用时,什么都没有!

我创建了一个简化版本的代码来说明逻辑,可以直接粘贴到 Playground 中。currentPage和页面变量只是为了演示流程。最后一个 print(( 语句不会被调用。让currentPage += 1遇到问题,并设置currentPage += 6以避免递归。显然,我在这里遗漏了一些基本概念。任何人?

import UIKit
let pages = 5
var currentPage = 0
class ThingsApi {
var url: URL?
var next: URL?
init(from url: URL) {
self.url = url
}
init() {
self.url = URL(string: "https://whatever.org")
}
func get(completion: @escaping (Data?, HTTPURLResponse?, Error?) -> Void) {
// *** Greatly simplified
// Essentially: use URLSession.shared.dataTask and download data async.
// When done, call the completion handler.
// Simulate that the download will take 1 second.
sleep(1)
completion(nil, nil, nil)
}
}
func downloadThings(url: URL? = nil, completion: @escaping (Bool, Error?, String?) -> Void) {
var thingsApi: ThingsApi
if let url = url {
// The ThingsApi will use the next url (retrieved from previous call).
thingsApi = ThingsApi(from: url)
} else {
// The ThingsApi will use the default url.
thingsApi = ThingsApi()
}
thingsApi.get(completion: { (data, response, error) in
if let error = error {
completion(false, error, "We have nothing")
} else {
// *** Greatly simplified
// Parse the data and save to db.
// Simulate that the thingsApi.next will have a value 5 times.
currentPage += 1
if currentPage <= pages {
thingsApi.next = URL(string: "https://whatever.org?page=(currentPage)")
}
if let next = thingsApi.next {
// Continue downloading things recursivly.
downloadThings(url: next) { (success, error, feedback) in
guard success else {
completion(false, error, "failed")
return
}
}
} else {
print("We are done")
completion(true, nil, "done")
print("I am sure of it")
}
}
})
}
downloadThings { (success, error, feedback) in
guard success else {
print("downloadThings() failed")
return
}
// THIS DOES NOT GET EXECUTED!
print("All your things have been downloaded")
}

这似乎只是"你忘了自己称呼它"的情况:)

在这里的这个if语句中:

if let next = thingsApi.next {
// Continue downloading things recursivly.
downloadThings(url: next) { (success, error, feedback) in
guard success else {
completion(false, error, "failed")
return
}
}
} else {
print("We are done")
completion(true, nil, "done")
print("I am sure of it")
}

想想最外层的downloadThings调用会发生什么,执行进入if分支,下载成功。completion从来不叫!

您应该在guard声明后致电completion

最新更新