swift AsyncStream未执行闭包

oaxa6hgo  于 2023-02-03  发布在  Swift
关注(0)|答案(1)|浏览(131)

所以我正在阅读www.example.com上的Modern Concurrency一书,我猜这本书一定是过时了,我试图在AsyncStream中运行closure,但似乎没有成功,我对异步/等待这件事还是很陌生的,raywenderlich.com我不是很熟悉警告的意思,只是想学习所有这些新的东西,我真的很感激一些帮助,有没有办法用斯威夫特6修复它?提前感谢! but when adding some breakpoints I can see my code is not getting there. This is my code and a screenshot with some warnings showing. I am not really familiar with what the warnings mean, just trying to learn all this new stuff, I would truly appreciate some help and is there a way to fix it with Swift 6? Thanks in advance!
在并发执行代码中引用捕获的变量"倒计时";这是Swift 6中的错误
并发执行代码中捕获的变量"倒计时"的变异;这是Swift 6中的错误

func countdown(to message: String) async throws {
    guard !message.isEmpty else { return }
    var countdown = 3

    let counter = AsyncStream<String> { continuation in
      Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
          continuation.yield("\(countdown)...")
          countdown -= 1
        }
    }

    for await countDownMessage in counter {
      try await say(countDownMessage)
    }
 }

monwx1rj

monwx1rj1#

Timer.scheduleTimer要求在运行循环中调度它。实际上,这意味着我们希望在主线程的运行循环中调度它。因此,您可以从主线程调用scheduleTimer,或者创建一个计时器并在主线程的RunLoop中手动调度它。请参阅Timerdocumentation的 * 在运行循环中调度计时器 * 部分。
您可以通过将此函数与主要参与者隔离来在主线程上获得此函数。

@MainActor
func countdown(to message: String) async throws { … }

这里还有一些其他的问题:
1.我建议在AsyncStream中定义countdown变量:

@MainActor
func countdown(to message: String) async throws {
    guard !message.isEmpty else { return }

    let counter = AsyncStream<String> { continuation in
        var countdown = 3
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
            continuation.yield("\(countdown)...")
            countdown -= 1
        }
    }

    for await countDownMessage in counter {
        try await say(countDownMessage)
    }
}
  1. AsyncStream永远不会结束。您可能希望在它到达零时结束它:
@MainActor
func countdown(to message: String) async throws {
    guard !message.isEmpty else { return }

    let counter = AsyncStream<String> { continuation in
        var countdown = 3
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
            continuation.yield("\(countdown)...")

            // presumably you want this countdown timer to finish when it hits zero

            guard countdown > 0 else {
                timer.invalidate()
                continuation.finish()
                return
            }

            // otherwise, decrement and carry on

            countdown -= 1
        }
    }

    for await countDownMessage in counter {
        try await say(countDownMessage)
    }
}

1.应该有一个continuation.onTermination闭包来处理异步序列的取消。

@MainActor
func countdown(to message: String) async throws {
    guard !message.isEmpty else { return }

    let counter = AsyncStream<String> { continuation in
        var countdown = 3
        let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
            continuation.yield("\(countdown)...")

            // presumably you want this countdown timer to finish when it hits zero

            guard countdown > 0 else {
                timer.invalidate()
                continuation.finish()
                return
            }

            // otherwise, decrement and carry on

            countdown -= 1
        }

        continuation.onTermination = { _ in
            timer.invalidate()
        }
    }

    for await countDownMessage in counter {
        try await say(countDownMessage)
    }
}

回到最初的问题(为什么它没有运行),我个人会避免将Timer与Swift并发一起使用。GCD计时器会更好,因为它不需要RunLoop。更好的是,我建议使用Task.sleep。不用说,这是为Swift并发设计的,而且是可以取消的。
我个人会建议这样的东西:

func countdown(to message: String) async throws {
    guard !message.isEmpty else { return }
    
    let counter = AsyncStream<String> { continuation in
        let task = Task {
            for countdown in (0...3).reversed() {
                try await Task.sleep(for: .seconds(1))
                continuation.yield("\(countdown)...")
            }
            
            continuation.finish()
        }
        
        continuation.onTermination = { _ in
            task.cancel()
        }
    }
    
    for await countDownMessage in counter {
        try await say(countDownMessage)
    }
}

相关问题