簡體   English   中英

Swift - 循環中的異步調用

[英]Swift - Async calls in loop

希望你一切順利。 我正在努力實現以下目標:

  1. 從數據庫中獲取數據數組(異步調用)
  2. 迭代獲取的數據數組
  3. 獲取有關每個 object 的附加信息(異步調用)
  4. 創建一個包含所有信息的新數據數組並將其返回

目前,我有以下方法

self.dataAccessService.fetchRepliesByCommentId(completionHandler: { (commentReplyArray) in
  for var i in 0..<commentReplyArray.count {
    let commentReply = commentReplyArray[i]
    let commentItem = CommentItem()
     
    self.fetchDetailsAboutCommentReply(commentReplyObject: commentReply) { (commentItem) in
      commentItem.commentObject = commentReply
       
      dataSource.insert(commentItem, at: index + i + 1) -> APP CRASHES HERE, i is never 0 here
      ips.append(IndexPath(row: index + i + 1 , section: 0))
                                                      
      if (i == commentReplyArray.count - 1) {
        self.delegate?.didLoadReplies(dataSource: dataSource, ips: ips)
      }
    }
  }
}, commentId: commentItem.commentObject.id)

我的 fetchDetailsAboutCommentReply function:

private func fetchDetailsAboutCommentReply(commentReplyObject:CommentReply, completionHandler:@escaping(CommentItem)->()) {
 let group = DispatchGroup()
 let commentItem = CommentItem()
 
 group.enter()
  self.dataAccessService.fetchUserById(completionHandler: { (userObject) in
    commentItem.userObject = userObject
    group.leave()
 }, uid: commentReplyObject.userId)
        
 group.enter()
  self.dataAccessService.fetchDownloadURLOfProfileImage(organizerId: commentReplyObject.userId) { (contentURL) in
  commentItem.userObject.contentURL = contentURL
  group.leave()
 }
 
group.notify(queue: .main) {
  completionHandler(commentItem)
}

}

我的問題是如何更改我的代碼,所以循環基本上“暫停”,直到我獲取迭代的 object 的所有詳細信息,將其添加到 dataSource 數組中,然后繼續下一個?

謝謝並保持健康!

很難具體說明,因為我們沒有關於您的數據源邏輯、類型等的信息。但是,再說一次,我認為我們不想在這里討論這個問題。

所以,一些一般性的觀察:

  • 您應該在循環中使用DispatchGroup 例如,

     let group = DispatchGroup() for i in... { group.enter() someAsyncMethod { completion in defer { group.leave() }... } } group.notify(queue: .main) {... }
  • 如您所見,我刪除了if (i == commentReplyArray.count - 1) {... } test 因為您希望這些並行運行並且僅僅因為“最后一個”完成並不意味着它們我都完成了。 使用DispatchGroup及其notify方法可以知道它們何時完成。

  • 我對您的dataSource.insert調用中的+ 1邏輯持懷疑態度(我們生活在一個從零開始的索引世界中)。 例如,您插入的第一個項目的索引應該是0 ,而不是1 (如果你正在執行+ 1邏輯,因為你的 tableview/collection 視圖中有一些額外的單元格,我建議不要在這個例程中糾纏偏移索引邏輯,而是讓你的“數據源”來處理。)

  • 這可能無關緊要,因為無論如何您確實想重構此數據源,因此調用fetchDetailsAboutComent完成處理程序的順序無關緊要。 例如,構建一個本地字典,完成后,構建排序數組並將其傳回:

     // dictionary for results, so order doesn't matter var results: [Int: CommentReply] = [:] // I don't know what the type of your comment/reply is, so adjust this as needed let group = DispatchGroup() for i in 0..<20 { group.enter() someAsyncMethod { completion in defer { group.leave() }... results[i] =... } } group.notify(queue: .main) { // now build array from the dictionary let array = (0..<20).compactMap { results[i] } dataSource?.insert(array)... }

    如果你真的想在結果進來時調用數據源,理論上你可以這樣做,但是你要確保你不只是插入到一個數組中,而是dataSource object 可以在結果進來時處理它們,亂序。

    您建議您希望循環“暫停”一個請求,直到前一個請求完成,我強烈建議不要使用這種模式,因為它會使過程慢得多(基本上會加劇網絡延遲效應)。 您確實需要可以讓請求並行運行的邏輯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM