Swift-循环中的异步调用



我希望你做得很好。我正在努力实现以下目标:

  1. 从数据库获取数据数组(异步调用(
  2. 对提取的数据数组进行迭代
  3. 获取有关每个对象的附加信息(异步调用(
  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)

我的fetchDetailsAutCommentReply函数:

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

}

我的问题是,我如何才能更改我的代码,所以循环基本上是"暂停";直到我获取迭代对象的每个细节信息,将其添加到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) { ... }测试,因为你希望这些测试并行运行,而仅仅因为"最后一个"完成并不意味着它们都完成了。使用DispatchGroup及其notify方法来了解它们何时全部完成。

  • 我怀疑您的dataSource.insert调用中的+ 1逻辑(我们生活在一个基于零的索引世界中(。例如,您插入的第一个项目的索引应该是0,而不是1。(如果你在做+ 1逻辑,因为你的表视图/集合视图中有一些额外的单元格,我建议不要把偏移索引逻辑纠缠在这个例程中,而是让你的"数据源"来处理。(

  • 这可能并不重要,因为无论如何,你真的想重构这个数据源,所以调用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对象可以在结果出现的时候处理结果,不按顺序。

    你建议你希望循环为一个请求"暂停",直到前一个请求完成,我强烈建议不要采用这种模式,因为这会使过程慢得多(基本上会加剧网络延迟效应(。您确实需要能够让请求并行运行的逻辑。

最新更新