也许我有一个理解整体前提的潜在问题,但我试图找出使用一系列项目做某事的最佳方法,并结束 test 一旦找到某个标准,就会提早。
例如,我有一系列名字;
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没有兴趣。)
谢谢!
答案 0 :(得分:3)
在开始循环之前,设置一个标志:
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 :(得分:1)
你的情况并不是一个循环的设计。在执行循环内的闭包之前,循环可能完成是可能的。
相反,尝试使用完成块的递归函数:
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)
}
}
}
这确保了无论响应块的同步性如何,一次只发生一次呼叫
答案 2 :(得分:0)
将@noescape
属性添加到TestName的闭包参数,以指示闭包不会转义该调用。
如果要停止,请在TestName调用之外使用变量,设置为false,并在循环内将其设置为true。
在TestName调用之后,检查变量以查看是否需要中断。
或
答案 3 :(得分:0)
我必须和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
}
}
}