Swift DispatchQueue.main.asyncAfter未延迟

Swift DispatchQueue.main.asyncAfter未延迟,swift,grand-central-dispatch,dispatch-async,Swift,Grand Central Dispatch,Dispatch Async,我的DispatchQueue.main.asyncAfter执行块未等待执行 我写了一个MacOS单视图应用程序。(代码12.0.1(12A7300))。它有一个for循环,调用从我的服务器下载内容的函数。我想限制这些请求。我正在尝试使用DispatchQueue.main.asyncAfter。但是for循环中的所有调用都是立即同时进行的。这是我的密码: func fetchDocuments() { for index in 651...660 { let docN

我的DispatchQueue.main.asyncAfter执行块未等待执行

我写了一个MacOS单视图应用程序。(代码12.0.1(12A7300))。它有一个for循环,调用从我的服务器下载内容的函数。我想限制这些请求。我正在尝试使用
DispatchQueue.main.asyncAfter
。但是for循环中的所有调用都是立即同时进行的。这是我的密码:

func fetchDocuments() {
    for index in 651...660 {
        let docNumber = String(index)
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            print(Date())
            self.fetchDocument(byNumber: docNumber)
        }
    }
}
当我运行此代码时,我在控制台上得到以下输出:

2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
2020-10-05 03:27:09 +0000
我正在从Xcode运行这段代码并观察控制台


任何帮助都将不胜感激。

'DispatchQueue.main.asyncAfter'是一个异步进程。在这里,您已经为每个语句编写了“.now()+2”。但是循环执行的时间非常短。因此,循环中的每一条语句都需要2秒的时间

请尝试下面的代码

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        fetchDocuments()
    }

    func fetchDocuments() {
        var count = 0
        for index in 651...660 {
            let docNumber = String(index)
            DispatchQueue.main.asyncAfter(deadline: .now() + (Double(count+1)*2.0)) {
                print(Timestamp().printTimestamp())
                self.fetchDocument(byNumber: docNumber)
            }
            count += 1
        }
    }
    
    func fetchDocument(byNumber: String) {
        print("Hello World")
    }
}


class Timestamp {
    lazy var dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS "
        return formatter
    }()

    func printTimestamp() {
        print(dateFormatter.string(from: Date()))
    }
}
输出:

2020-10-05 10:41:18.473
()
Hello World
2020-10-05 10:41:18.475
()
Hello World
2020-10-05 10:41:18.475
()
Hello World
2020-10-05 10:41:18.475
()
Hello World
2020-10-05 10:41:18.475
()
Hello World
2020-10-05 10:41:18.475
()
Hello World
2020-10-05 10:41:18.475
()
Hello World
2020-10-05 10:41:18.476
()
Hello World
2020-10-05 10:41:18.476
()
Hello World
2020-10-05 10:41:18.476
()
Hello World

asyncAfter
的调用会立即返回,这意味着当您加速循环时,所有这些迭代从现在起将有效地触发2秒,而不是两秒

还有一些次要问题,当您使用
asyncAfter
时,如果对象被解除分配并且您想要停止该过程,则取消它们会有点繁琐。此外,如果您提前计划所有这些
asyncAfter
,您将受到计时器合并的影响(当后一个计划的事件彼此之间的距离在10%以内时,计时器合并会显示出来;651…660的范围没有问题,但如果使用更大的范围,计时器合并会显示出来)

几种常见的解决方案包括:

  • 递归模式将确保每次迭代在前一次迭代完成两秒钟后触发:

    func fetchDocuments<T: Sequence>(in sequence: T) where T.Element == Int {
        guard let value = sequence.first(where: { _ in true }) else { return }
    
        let docNumber = String(value)
        fetchDocument(byNumber: docNumber)
    
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
            self?.fetchDocuments(in: sequence.dropFirst())
        }
    }
    
  • 另一种方法是使用计时器:

    func fetchDocuments<T: Sequence>(in sequence: T) where T.Element == Int {
        var documentNumbers = sequence.map { String($0) }
    
        let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
            guard
                let self = self,
                let documentNumber = documentNumbers.first
            else {
                timer.invalidate()
                return
            }
    
            self.fetchDocument(byNumber: documentNumber)
            documentNumbers.removeLast()
        }
        timer.fire() // if you don't want to wait 2 seconds for the first one to fire, go ahead and fire it manually
    }
    
    func fetchDocuments(按顺序:T),其中T.Element==Int{
    var documentNumbers=sequence.map{String($0)}
    让timer=timer.scheduledTimer(withTimeInterval:2,repeats:true){[weak self]timer in
    警卫
    让自我=自我,
    让documentNumber=documentNumbers.first
    否则{
    timer.invalidate()
    返回
    }
    self.fetchDocument(按编号:documentNumber)
    documentNumbers.removeLast()
    }
    timer.fire()//如果您不想等待2秒钟第一个计时器启动,请继续手动启动
    }
    

  • 这两个(a)将在每次调用之间提供两秒的间隔,(b)消除计时器合并风险;和(c)将取消,如果您关闭所讨论的对象。

    它确实会延迟,但会将所有对象延迟2秒。在
    for
    循环中使用
    asyncAfter
    ,该问题已被多次询问和回答。你可能想搜索自己,但这里有一个类似的问题。如果你想限制请求,那么你可以在每次下载完成后调用“syncAfter”-但是如果你真正想的是一次下载一个文件,按顺序,然后,您只需在当前文件的完成处理程序中调用下载下一个文件,感谢您使用计时器和post将延迟乘以for循环的索引/计数,为我指明了正确的方向。我搜索了SO,但我没有使用正确的搜索词。
    func fetchDocuments<T: Sequence>(in sequence: T) where T.Element == Int {
        var documentNumbers = sequence.map { String($0) }
    
        let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
            guard
                let self = self,
                let documentNumber = documentNumbers.first
            else {
                timer.invalidate()
                return
            }
    
            self.fetchDocument(byNumber: documentNumber)
            documentNumbers.removeLast()
        }
        timer.fire() // if you don't want to wait 2 seconds for the first one to fire, go ahead and fire it manually
    }