Swift中嵌套的异步调用

Max*_*aev 2 dispatch grand-central-dispatch dispatch-async swift

我对编程很新,所以我有一个简单的问题.实际上,写作可以帮助我更快地识别问题.

无论如何,我有一个多个异步调用的应用程序,它们嵌套如下:

InstagramUnoficialAPI.shared.getUserId(from: username, success: { (userId) in

    InstagramUnoficialAPI.shared.fetchRecentMedia(from: userId, success: { (data) in

        InstagramUnoficialAPI.shared.parseMediaJSON(from: data, success: { (media) in

                guard let items = media.items else { return }
                self.sortMediaToCategories(media: items, success: {

                    print("success")

          // Error Handlers
Run Code Online (Sandbox Code Playgroud)

看起来很可怕,但那不是重点.一旦我开始工作,我将调查Promise Kit.

我需要sortMediaToCategories等待完成然后重新加载我的集合视图.但是,在sortMediaToCategories我有另一个嵌套函数,它也是异步并且具有for循环.

func sortMediaToCategories(media items: [StoryData.Items],
                           success: @escaping (() -> Swift.Void),
                           failure: @escaping (() -> Swift.Void)) {

    let group = DispatchGroup()
    group.enter()


    for item in items {


        if item.media_type == 1 {

            guard let url = URL(string: (item.image_versions2?.candidates?.first!.url)!) else {return}

            mediaToStorageDistribution(withImageUrl: url,
                                       videoUrl: nil,
                                       mediaType: .jpg,
                                       takenAt: item.taken_at,
                                       success: { group.notify(queue: .global(), execute: {

                                        self.collectionView.reloadData()
                                           group.leave()

                                       })  },
                                       failure: { print("error") })

 //....
Run Code Online (Sandbox Code Playgroud)

我无法承担集合视图每次显然重新加载,所以我需要等待循环完成然后重新加载.

我正在尝试使用Dispatch Groups,但正在努力解决它.你能帮帮我吗?任何简单的例子和​​任何建议将非常感激.

Cou*_*per 5

您面临的问题是一个常见的问题:拥有多个异步任务并等待所有任务完成.

有一些解决方案.最简单的是利用DispatchGroup:

func loadUrls(urls: [URL], completion: @escaping ()->()) {
    let grp = DispatchGroup()

    urls.forEach { (url) in
        grp.enter()
        URLSession.shared.dataTask(with: url) { data, response, error in
            // handle error
            // handle response
            grp.leave()
        }.resume()
    }

    grp.notify(queue: DispatchQueue.main) {
        completion()
    }
}
Run Code Online (Sandbox Code Playgroud)

该函数loadUrls是异步的,并且期望一个URL数组作为输入,并且在完成所有任务时将调用一个完成处理程序.这将通过所示的完成DispatchGroup.

最关键的是,要确保grp.enter()将被调用之前调用任务,grp.leave当任务被称为已经完成.enter并且leave应该是平衡的.

grp.notify最后注册一个闭包,当DispatchGroup grp平衡时(即,它的内部计数器达到零),将在指定的调度队列(此处为:main)上调用该闭包.

但是,这个解决方案有一些注意事项:

  • 所有任务几乎同时启动并同时运行
  • 此处未显示通过完成处理程序报告所有任务的最终结果.它的实现需要适当的同步.

对于所有这些警告,有很好的解决方案应该使用合适的第三方库来实现.例如,您可以将任务提交给某种"执行者",该"执行者"控制并发运行的任务数(与OperationQueue和async Operations匹配).

许多"Promise"或"Future"库简化了错误处理,并且只需一个函数调用就可以帮助您解决这些问题.