为链式函数使用 Swift 完成处理程序

Posted

技术标签:

【中文标题】为链式函数使用 Swift 完成处理程序【英文标题】:Working with Swift completion handlers for chained functions 【发布时间】:2018-07-02 18:44:34 【问题描述】:

我将一些函数链接在一起,一旦所有函数都运行完毕,我不知道如何调用带有返回值的完成处理程序。

class AirQualityProvider 
    var aBlock: ((Int?) -> Void)?

    func getAirQuality(completion: @escaping (Int?) -> Void) 
        aBlock = completion
        callAPI()
    

    private func callAPI() 
        let data = Data()
        parseDataForAQI(data: data)
    

    private func parseDataForAQI(data: Data) 

        for d in data 
            dosomeMath(d)
        
    


    private func dosomeMath(data: Int) 

        // HERE IS WHERE I WANT IT TO SUM UP ALL THE NUMBERS
        THEN ONLY RETURN ONE VALUE using a completion handler.
        Currently, it returns the average as it is being generated.
    

在 Alexander 的帮助下几乎成功了。 Alexander 提供的代码完美运行,令人惊叹。问题是,当我在 alamofire 中运行 taskrunner 时,它返回空。在 alamofire 之外它照常工作。我需要在 alamofire 中运行它。

func A(json : JSON)
    for (key,subJson) in json
        if subJson["free"].doubleValue > 0.0 
            func B(asset: subJson["asset"].stringValue, json: subJson)
        
    

    print(taskRunner.getResults())



func B(asset : String, json : JSON)

        //OUTSIDE ALAMOFIRE WORKS
            self.taskRunner.execute
            return 100
        

    Alamofire.request(url).responseJSON  response in

       //INSIDE ALAMOFIRE DOESN'T WORK. Returns []
            self.taskRunner.execute
            return 100
        

    


【问题讨论】:

这听起来像是 DispatchGroup 的工作 感谢您的意见。 DispatchGroup 函数究竟是如何工作的?谢谢! DispatchGroups 将等待所有异步调用完成,然后可能继续执行或调用通知函数。您能否从每个 doSomeMath 返回一些值,并将所有 mathResults 以某种方式组合成一个数字,然后在 parseDataForAQI(data:) 中调用 aBlock(numberPutInto) 之类的闭包? @Red 你能把你的想法写成代码吗? ...考虑到函数“dosomemath”在循环中被调用,我看不出调度组如何解决这个问题。 @Alexander 请查看上面关于我使用 dispatchgroup 问题的评论 【参考方案1】:

我将使用调度队列来同步结果的聚合(通过同步Array.append(_:) 调用和随后的数组读取)。这是一个简单的例子:

import Dispatch
import Foundation

class ParallelTaskRunner<Result> 
    private var results = [Result]()
    private let group = DispatchGroup()
    private let resultAggregatorQueue = DispatchQueue(label: "Result Aggregator")

    func execute(_ closure: (@escaping (Result) -> Void) -> Void) 
        group.enter() // Register that a new task is in-flight
        closure  result in
            self.resultAggregatorQueue.sync  // Synchronize access to the array
                self.results.append(result) // Record the result
            
            self.group.leave() // This task is done
        
    

    func getResults() -> [Result] 
        group.wait() // Make sure all in-flight tasks are done
        return resultAggregatorQueue.sync  return results 
    





let taskQueue = DispatchQueue(label: "Task Queue", attributes: .concurrent)
let taskRunner = ParallelTaskRunner<Int>()
for i in 0...100 
    taskRunner.execute  completionHandler in 
        taskQueue.async  // Simulated async computation
            let randomTime = 3.0
            print("Sleeping for \(randomTime)")
            Thread.sleep(forTimeInterval: randomTime) // Simulates intesnive computation
            let result = i // Simulate a result
            completionHandler(result)
        
    

print(taskRunner.getResults()) // Oh look, all the results are here! :D

【讨论】:

感谢您的意见,看来这可行。考虑到我的课程非常长,我只是对如何将它应用到我的代码感到困惑。 @tenkdarko 为什么你的课很长很重要? (尽管这很好地表明您应该通过将其分解为更小、更简单的部分来简化它) 嗯,你能告诉我如何将此解决方案应用于我当前的问题吗?代码看起来不错,只是没有看到连接。 @tenkdarko 我不能特别具体,因为你的例子很模糊。但这里的重点是您可以遍历所有案例,使用我的ParallelTaskRunner 中的execute 方法对它们进行API 调用,然后您可以使用getResults() 收集所有结果,然后您可以将这些结果相加(或根据需要组合它们),并返回单个结果。 非常感谢您的帮助。你的代码太棒了。请查看我的编辑以查看我在使用您的代码时遇到的问题。您的代码可以完美运行,只是无法在 alamofire 中运行。它返回一个空数组。

以上是关于为链式函数使用 Swift 完成处理程序的主要内容,如果未能解决你的问题,请参考以下文章

从 Java Script (TVML) 调用带有完成处理程序的 Swift 函数

使用完成处理程序(闭​​包)语法从objective-c文件调用swift文件中的函数

从 swift 评估 javascript 函数,在完成处理程序中得到 nil

从 Timer 选择器函数 Swift 调用函数的完成处理程序

Swift-使用完成处理程序更新闭包外的全局变量

完成处理程序 swift 3 从函数返回一个变量