如果我有多个异步调用,何时调用我的完成块

时间:2018-03-18 14:43:13

标签: swift asynchronous closures

我对如何使用闭包有点困惑,或者在我的情况下有效地使用完成块。在我的情况下,我想在完成一些异步调用时调用一段代码,让我的调用者知道是否有错误或成功等等。

所以我想要完成的一个例子可能如下所示:

// Caller
updatePost(forUser: user) { (error, user) in 
   if let error = error {
      print(error.description) 
   }

   if let user = user {
      print("User was successfully updated")
      // Do something with the user...
   }
}   


public func updatePost(forUser user: User, completion: @escaping (Error?, User?) -> () {

     // Not sure at what point, and where to call completion(error, user)
     // so that my caller knows the user has finished updating

     // Maybe this updates the user in the database
     someAsyncCallA { (error)

     }

     // Maybe this deletes some old records in the database
     someAsyncCallB { (error)

     }

}

理想情况下,我希望在异步块B完成时调用完成块(假设异步块A已经完成,我知道这是一个BAD假设)。但是,在异步块B首先完成并且异步块A需要更长时间的情况下会发生什么?如果我在异步块B之后调用完成,那么我的调用者认为该方法已经完成。

在这样的情况下,假设我想在更新完成时告诉用户,但我只知道两个异步块完成时它已经完成。我如何解决这个问题,或者我只是使用了错误的闭包?

2 个答案:

答案 0 :(得分:0)

我不知道您的问题是否已经回答。我认为您正在寻找的是DispatchGroup。

let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
someAsyncCallA(completion: {
    dispatchGroup.leave()
})
dispatchGroup.enter()
someAsyncCallB(completion: {
    dispatchGroup.leave()
})
dispatchGroup.notify(queue: .main, execute: {
    // When you get here, both calls are done and you can do what you want. 
})

非常重要的注意事项:enter()leave()呼叫必须保持平衡,否则您将因异常而崩溃。

答案 1 :(得分:-1)

尝试以下代码:

public func updatePost(forUser user: User, completion: @escaping (Error?, User?) -> () {

     // Not sure at what point, and where to call completion(error, user)
     // so that my caller knows the user has finished updating

     // Maybe this updates the user in the database
     someAsyncCallA { (error)
         // Maybe this deletes some old records in the database
         someAsyncCallB { (error)
             completion()
         }
     }
}

请尝试以下更新的答案:

public func updatePost(forUser user: User, completion: @escaping (Error?, User?) -> () {
    var isTaskAFinished = false
    var isTaskBFinished = false
         // Not sure at what point, and where to call completion(error, user)
         // so that my caller knows the user has finished updating

    // Maybe this updates the user in the database
    someAsyncCallA { (error)
          // Maybe this deletes some old records in the database
          isTaskAFinished = true
          if isTaskBFinished{
              completion()
          }
    }

    someAsyncCallB { (error)
         isTaskBFinished = true
         if isTaskAFinished{
               completion()
         }
    }
}