使用完成处理程序结束循环的方法?

Method For Ending A Loop With Completion Handler?

也许我在理解总体前提方面存在潜在问题,但我正在尝试找出最佳方法来对一系列项目做一些事情,然后结束 测试一旦找到某个标准就早点。

例如,我有一个名字数组;
var names = ["Bob", "Billy", "Sarah", "Brandon", "Brian", "Rick"]

我想测试数组中的每个名称,看看它是否存在于数据库中。为此,我使用完成处理程序调用另一个函数;

for name in names {

    TestName(name) { response in

     if response {
         // END THE LOOP
     } else {
         // KEEP GOING
     }
 }

我无法弄清楚 // END THE LOOP。出于本示例的目的,我只关心第一次响应为真时(如果 Billy 存在于数组中,我对测试 Sarah、Brandon、Brian 或 Rick 没有进一步的兴趣)。

谢谢!

在开始循环之前,设置一个标志:

var exitEarly = false
for name in names {

每次通过循环测试标志:

for name in names {
    if exitEarly {
        break
    }

在 TestName 响应块中,设置标志:

TestName(name) { response in
    if response {
        exitEarly = true
    } else {
        // KEEP GOING
    }
}

但是请注意,如果 TestName 的块是异步执行的,那将不起作用,因为整个循环先于任何异步块的调用(这是异步性的本质)。

  1. 在TestName的闭包参数中添加@noescape属性,表示闭包不逃逸调用

  2. 在TestName调用之外使用一个变量,设置为false,如果要停止,在循环内部设置为true。

  3. 调用TestName后,检查变量是否需要中断

  1. 将 TestName 更改为 return 一个指示是否应该继续的值

你的情况并不是设计循环的真正目的。循环可能会在循环内的闭包执行之前完成。

相反,尝试使用带有完成块的递归函数:

func databaseHasAName(names: [String], index: Int, completion: (String?) -> ()) {

    guard index < names.count else{
        completion(nil)
        return
    }

    let name = names[index]
    TestName(name) { response in

        if response {
            completion(name)
        } else {
            databaseHasName(names, index: index + 1, completion: completion)
        }
    }
}

这确保一次只发生一个调用,而不管响应块的同步性如何

我不得不和 PEEJWEEJ 做同样的事情。我的异步任务是 Web 查询和解析并且非常密集,for 循环总是在这些任务完成之前完成,所以没有办法停止它们。

所以我将它转换为递归并在我希望它停止时设置一个标志,现在它工作正常。

// ************************************************************************************************************
// MARK: Recursive function
// I changed the loop to be recursive so I could stop when I need to - i.e. when selecting another race
// I set the stopFetching flag in the seque when changing races and the current set of BG queries stops
// ************************************************************************************************************
func getRaceResultsRecursive(race: Race, bibs: [Bib], index: Int, completion: @escaping (Bool?) -> ())
{
    guard index < bibs.count else{
        completion(nil)
        return
    }

    var url: URL
    self.stopFetching = false

    let bibID = bibs[index]
        url = self.athleteResultAPI.formatURL(baseURL: (race.baseURL)!,
                                              rd: (race.rdQueryItem)!,
                                              race: (race.raceQueryItem)!,
                                              bibID: "\(bibID.bib!)",
            detail: (race.detailQueryItem)!,
            fragment: (race.detailQueryItem)!,
            webQueryType: (race.webQueryType!))

    self.athleteResultAPI.fetchAthleteResults(url: url, webQueryType: race.webQueryType!)
    {
        (athleteReturnCode) in
        switch athleteReturnCode
        {
        case let .success(athleteResult):
            self.athleteResults.append(athleteResult)       // Add to collection
            self.delegate?.athleteResultsUpdated()           // update Delegate to notify of change
        case let .failure(error):
            print(error)
            break
        }

        if self.stopFetching {
            completion(true)
        }
        else {
            self.getRaceResultsRecursive(race: race, bibs: bibs, index: index + 1, completion: completion)
        }
    }
}

// ************************************************************************************************************
// getRaceResults
// Get all the bibs to track for this race from the iCloudKit database
// ************************************************************************************************************
func getRaceResults(race: Race)
{
    // get all the races and bibs an put in the Races Store
    raceStore.fetchBibsForRace(race: race, foreignKey: race.recordID)
    {
        (results, error) in
        if let error = error {
            print("Error in fetchBibsForRace(): \(error)")
            return
        }

        // clear the store since we are on a new race
        self.athleteResults.removeAll()
        self.getRaceResultsRecursive(race: race, bibs: race.bibs, index: 0)
        {
            (stopFetching) in
            return
        }

    }
}