如何使用 swift 中异步函数的数据退出 for 循环?

How to exit a for loop with data from an async function in swift?

函数findMediaLimit return 最高的i,从cURL接收到?

class func findMediaLimit(IgBusinessAccount: String, token: String) {
    let igBId = IgBusinessAccount
        
    for i in 1...12 {
        guard let encodedUrl = self.buildURLAPIGraph(IgBusinessAccount: igBId, token: token, i: i) else { return }
            
        //Async function that returns different i's in random order
        self.cURL(urlT: encodedUrl) { (result) in
        print(i)   
        //return the highest i             
        }
    }
}

我创建此功能是为了过滤转换为企业 Instagram 帐户后发布的媒体。

这是我的 cURL 函数

class func cURL (urlT: String,Completion block: @escaping ((OfficialProfile) -> ())) {
    
    //visuJson (urlT: urlT)
    
    GetJson.loadJson(fromURLString: urlT) { (result) in
        switch result {
        case .success(let data):
            //Parse
            do {
                let decodedData = try JSONDecoder().decode(OfficialProfile.self, from: data)
                if decodedData.username != nil {
                    block(decodedData)
                }
            } catch {
                print("decode error: ",error)
            }
        case .failure(let error):
            print("loadJson error:", error)
        }
    }
}

那是我的 loadJson 函数

class func loadJson(fromURLString urlString: String,
                          completion: @escaping (Result<Data, Error>) -> Void) {
        if let url = URL(string: urlString) {
            let urlSession = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
                if let error = error {
                    completion(.failure(error))
                }
                
                if let data = data {
                    completion(.success(data))
                }
            }
            urlSession.resume()
        }
    }

要了解您显然正在尝试执行的操作存在什么问题,让我们以更简单的方式进行模拟。我将使用异步随机数生成器。在操场上试试这个:

func delay(_ delay:Double, closure:@escaping ()->()) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func asyncRand(_ completion: @escaping (Int) -> ()) {
    let d = Double.random(in: 1...3)
    delay(d) {
        let i = Int.random(in: 1...100)
        completion(i)
    }
}
func go() {
    for i in 1...12 {
        asyncRand() { result in
            print(i, result)
        }
    }
    print("finished")
}
go()

典型 运行 的结果可能是:

finished
8 13
1 15
9 9
10 56
7 57
3 87
2 70
11 88
6 82
12 16
4 81
5 46

所以这里有两个问题,因为中央 asyncRand 调用的异步性质:随着时间的推移,结果无序地返回,以及 go 方法本身(循环) 在 any 结果返回之前完成,因此没有机会找出哪个结果是最大值。

为了解决这个问题,在我们等待 async/await 出现在 Swift 期间(可能最快下周),您可以使用调度组。忽略任何线程问题,我们可以这样做:

func go() {
    let group = DispatchGroup()
    var pairs = [[Int]]()
    for i in 1...12 {
        group.enter()
        asyncRand() { result in
            print(i, result)
            pairs.append([i,result])
            group.leave()
        }
    }
    group.notify(queue: DispatchQueue.main) {
        print("finished")
        if let maximum = pairs.max(by: {[=12=][1] < [1]}) {
            print(maximum)
        }
    }
}

现在的结果是(例如):

11 52
8 6
2 1
4 6
12 77
1 88
7 45
9 36
6 25
3 22
10 78
5 33
finished
[1, 88]

所以你看我们已经“等待”直到所有结果都返回,并且我们已经挑选出返回最大值的对。

所以你可以按照这些思路做一些事情。但这是否是个好主意是另一个问题。