Swift 使用信号量同步while循环内的嵌套异步网络请求

Swift 使用信号量同步while循环内的嵌套异步网络请求,swift,asynchronous,networking,request,semaphore,Swift,Asynchronous,Networking,Request,Semaphore,我有一个func,可以得到一个玩家列表。当我取回球员时,我只需要显示那些属于当前球队的球员,所以我只通过过滤他们来显示原始列表的一个子集。我事先不知道,在发出请求之前,有多少玩家属于用户选择的团队,因此我可能需要执行其他请求,直到我可以在TableView上显示至少10行玩家。用户从TableView的底部向上拉可以请求显示更多的播放器。为此,我调用了第一个异步func请求,该请求在一段时间内又调用了另一个嵌套的异步func请求。下面是一段代码,让您了解我正在尝试做什么: let semaph

我有一个func,可以得到一个玩家列表。当我取回球员时,我只需要显示那些属于当前球队的球员,所以我只通过过滤他们来显示原始列表的一个子集。我事先不知道,在发出请求之前,有多少玩家属于用户选择的团队,因此我可能需要执行其他请求,直到我可以在TableView上显示至少10行玩家。用户从TableView的底部向上拉可以请求显示更多的播放器。为此,我调用了第一个异步func请求,该请求在一段时间内又调用了另一个嵌套的异步func请求。下面是一段代码,让您了解我正在尝试做什么:

 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()
        }
    }
}