所以我有一个分页的API,每次用户进入屏幕时我都要调用它。下面的代码工作正常,但有一个问题,如果说API调用正在进行中,用户后退并再次出现,那么两个执行同时开始。并且必须等待两个执行完成。所以,我想要的是停止正在进行的API调用,并开始新的请求时,新的一个如果此设置是错误的,以任何方式请通知我,以及我做错了什么?
func storeAllData(matchID: Int64) {
GlobalVariable.isFetchingData = true
dbManager = DbManager.shared
if let lastScore = dbManager.getLastBallForCurrentMatch(matchID: matchID) {
self.fetchAllScore(scoreID: lastScore.id ?? 0, matchID: matchID)
} else {
self.fetchAllScore(scoreID: 0, matchID: matchID)
}
}
func fetchAllScore(scoreID: Int = 0, matchID: Int64) {
let id = matchID
backgroundQueue.async { [self] in
autoreleasepool {
fetchPaginatedData(matchID:Int(id), scoreID: scoreID, page: 1)
dispatchGroup.wait()
print("All API calls finished")
}
}
}
func fetchPaginatedData(matchID: Int,scoreID: Int = 0, page: Int) {
dispatchGroup.enter()
MatchesNetworkManager.sharedInstance.getAllScore(matchID: Int64(matchID), scoreID: Int64(scoreID), page: Int64(page), completionHandler: { [weak self] (allSocre, isSuccess, errorStr) in
if isSuccess {
if allSocre?.match?.scores != nil {
self?.oldScores.append(contentsOf: ((allSocre?.match?.scores)!))
self?.backgroundQueue.async {
if let scores = allSocre?.match?.scores as? [Scores] {
self?.dbManager.insertRecords(matchID: matchID, records: scores)
DispatchQueue.main.async {
self?.oldScores = []
if allSocre?.page_num ?? 0 == 1 {
self?.updateScoreView()
}
if (allSocre?.page_num ?? 0) < (allSocre?.total_pages ?? 0) {
self?.fetchPaginatedData(matchID: matchID,page: (allSocre?.page_num ?? 1) + 1)
} else {
// All pages have been fetched
GlobalVariable.isFetchingData = false
print("All API calls finished")
NotificationCenter.default.post(name: .refreshMarketView, object: nil, userInfo: nil)
self?.dispatchGroup.leave()
}
}
}
}
} else {
self?.updateScoreView()
GlobalVariable.isFetchingData = false
}
} else {
print("API call error: \(errorStr)")
self?.updateScoreView()
GlobalVariable.isFetchingData = false
self?.dispatchGroup.leave()
}
})
}
字符串
2条答案
按热度按时间nimxete21#
在这些(以及类似的)平台上,大多数并发抽象的现实是,一旦启动,工作单元几乎完全无法被强制取消(除了终止进程)。
通常处理这种情况的方法是检查工作单元本身 * 内部 * 的标志,指示它们应该被取消,此时它们可以进行任何需要的清理(如果有的话),并提前返回。如果您有一个调度组,您可以将标志与该组关联,然后在每个操作开始时(或在操作内循环的每次迭代的顶部)显式地检查标志,并且如果组被取消则提前返回。
你可以临时这么做。你可以使用现有的抽象(比如
NSOperation
'scancel
method),但即使这样,你也会注意到 Discussion 部分的第一行说:“This method does not force your operation code to stop.”你的操作代码仍然必须检查标志。例如,您可以使用
NSBlockOperation
,创建操作示例,然后捕获对执行真实的工作的块闭包内的NSBlockOperation
示例的零弱引用,然后使用该引用检查NSBlockOperation
是否已被取消(或释放)。长话短说,这里没有免费的午餐。
i5desfxk2#
从本质上讲,您似乎在寻找可取消的异步任务。(派遣组,使用
wait
阻塞线程直到请求完成)。我猜理论上可以通过为URLSession
请求保存URLSessionTask
对象来完成(我假设它们被埋在你的“网络管理器”中)和cancel
那些当有问题的视图消失时。但那是不优雅的。回到过去,建议是将异步任务 Package 在
Operation
子类中,尽量避免使用调度组,执行适当的isFinished
,isExecuting
KVO通知。您的Operation
子类将覆盖cancel
以取消网络请求。参见https://stackoverflow.com/a/40560463/1271826示例。无论如何,如果实现这类Operation
子类来 Package 异步工作,则取消工作相当简单。但是,在建议了如何使用遗留API实现这一点之后,(即操作队列),我必须承认,我不会考虑第二,现在。相反,我们会达到Swift并发。这不仅管理异步任务的依赖关系,但是标准
URLSession
方法data(from:)
和data(for:)
本身支持消除。(这是假设你甚至使用URLSession
;你没有和我们分享相关的代码。)你最终会得到更简单的代码,处理异步依赖关系,并支持取消。但这将需要更彻底的重写代码,你没有分享,所以我们甚至不能开始向你提供具体的建议。但是请参阅WWDC视频Meet async/await in Swift和Swift concurrency: Update a sample app(以及其中相应的“相关视频”部分中链接的其他视频)。在下面的评论中,你注意到你正在使用Alamofire。它使用
URLSession
并支持Swift并发。例如,考虑Alamofire的这种传统方法:
字符串
相反,你可以使用Alamofire采用Swift并发,现在你有了一个可取消的API:
型
使用此可取消的格式副本,您可以执行以下操作:
型
请参阅https://github.com/robertmryan/AlamofireWithSwiftConcurrency,了解Minimal, Reproducible Example对比GCD和Swift并发方法。
现在,我认识到这与您的示例有很大的不同(因为您没有充分分享您的实现),所以不要太迷失在我的示例中,但请注意: