繁体   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