Swift 使用信号量同步while循环内的嵌套异步网络请求
我有一个func,可以得到一个玩家列表。当我取回球员时,我只需要显示那些属于当前球队的球员,所以我只通过过滤他们来显示原始列表的一个子集。我事先不知道,在发出请求之前,有多少玩家属于用户选择的团队,因此我可能需要执行其他请求,直到我可以在TableView上显示至少10行玩家。用户从TableView的底部向上拉可以请求显示更多的播放器。为此,我调用了第一个异步func请求,该请求在一段时间内又调用了另一个嵌套的异步func请求。下面是一段代码,让您了解我正在尝试做什么:Swift 使用信号量同步while循环内的嵌套异步网络请求,swift,asynchronous,networking,request,semaphore,Swift,Asynchronous,Networking,Request,Semaphore,我有一个func,可以得到一个玩家列表。当我取回球员时,我只需要显示那些属于当前球队的球员,所以我只通过过滤他们来显示原始列表的一个子集。我事先不知道,在发出请求之前,有多少玩家属于用户选择的团队,因此我可能需要执行其他请求,直到我可以在TableView上显示至少10行玩家。用户从TableView的底部向上拉可以请求显示更多的播放器。为此,我调用了第一个异步func请求,该请求在一段时间内又调用了另一个嵌套的异步func请求。下面是一段代码,让您了解我正在尝试做什么: let semaph
let semaphore = DispatchSemaphore(value: 0)
func getTeamPlayersRequest() {
service.getTeamPlayers(...)
{
(result) in
switch result
{
case .success(let playersModel):
if let validCurrentPage = currentPageTmp ,
let validTotalPages = totalPagesTmp ,
let validNextPage = self.getTeamPlayersListNextPage()
{
while self.playersToShowTemp.count < 10 && self.currentPage < validTotalPages
{
self.currentPage = validNextPage //global var
self.fetchMorePlayers()
self.semaphore.wait() //global semaphore
}
}
case .failure(let error):
//some code...
}
})
}
private func fetchMorePlayers(){
// Completion handler of the following function is never called..
service.getTeamPlayers(requestedPage: currentPage, completion: {
(result) in
switch result
{
case .success(let playersModel):
if let validPlayerList = playersList,
let validPlayerListData = validPlayerList.data,
let validTeamModel = self.teamPlayerModel,
let validNextPage = self.getTeamPlayersListNextPage()
{
for player in validPlayerListData
{
if ( validTeamModel.id == player.team?.id)
{
self.playersToShowTemp.append(player)
}
}
}
self.currentPage = validNextPage
self.semaphore.signal() //global semaphore
case .failure(let error):
//some code...
}
}
}
let信号量=调度信号量(值:0)
func getTeamPlayersRequest(){
服务。getTeamPlayers(…)
{
(结果)在
切换结果
{
成功案例(让玩家示范):
如果让validCurrentPage=currentPageTmp,
设validTotalPages=totalPagestp,
让validNextPage=self.GetTeamPlayerListNextPage()
{
而self.playersToShowTemp.count<10和self.currentPage
我尝试了DispatchGroup和Semaphore,但我不知道我做错了什么。我调试了代码,发现第一个异步调用在不同的队列(不是主队列)中执行另一个线程。嵌套异步调用getexecuted在另一个线程上,但我不知道它是否与第一个异步调用的并发队列相同
ESTED调用的完成处理程序从未被调用。有人知道为什么吗?self.semaphore.wait(),即使它在fetchMorePlayers()返回后执行,是否阻止/阻止调用嵌套的异步完成处理程序
我通过调试器注意到Xcode vars窗口中的completion()有一个注释“swift partial apply forclosure转发器#1”如果我们在循环中内联函数调用,它看起来像这样:
而self.playersToShowTemp.count<10&&self.currentPage
因此,nbaService.getTeamPlayers
调度一个请求,可能在主DispatchQueue
上,并立即返回。然后,您在信号量上调用wait
,这可能会在GCD试图运行由nbaService.getTeamPlayers
调度的任务之前阻塞
这是DispatchQueue.main
上的一个问题,它是一个串行队列。它必须是一个串行队列,UI更新才能工作。通常发生的情况是在运行循环的某个迭代中,您发出请求,然后返回..该气泡返回到运行循环,该循环检查更多事件和排队的任务。在这种情况下,当您完成getTeamPlayersRequest
中的andler正在等待运行,运行循环(通过GCD)为该迭代执行它。然后您阻塞主线程,因此运行循环无法继续。如果确实需要阻塞,请始终在不同的调度队列上执行,最好是在.concurrent
队列上执行
有时会对.async
的作用产生混淆。它只意味着“稍后运行此操作,然后立即将控制权返回给调用方”。仅此而已。它不保证闭包将同时运行。它只是计划稍后(可能很快)运行它在您调用它的任何DispatchQueue
上。如果该队列是串行队列,则它将排队在该调度队列的运行循环中依次运行。如果它是并发队列(即您专门将属性设置为包含.concurrent
)。然后它将运行,可能与同一调度队列上的其他任务同时运行
为了避免这种情况,您可以使用异步链接,而不是使用循环
private func fetchMorePlayers(条件为@autoclosure@escaping()->Bool){
保护条件()else{return}
nbaService.getTeamPlayers(requestedPage:currentPage,completion:{
(结果)在
切换结果
{
成功案例(让玩家示范):
如果让validPlayerList=PlayerList,
设validPlayerListData=validPlayerList.data,
让validTeamModel=self.teamPlayerModel,
让validNextPage=self.GetTeamPlayerListNextPage()
{
对于玩家来说
func startFetching(@escaping completion: () -> Void) {
fetchPlayers(page: 0, completion: completion)
}
private func fetchPlayers(page: Int, @escaping completion: () -> Void) {
// prepare request
// now perform request
performRequest(...) { ...
if let error = error {
completion()
return
}
...
if doesNeedMorePlayers {
fetchPlayers(page: page + 1, completion: completion)
} else {
completion()
}
}
}