поэтому я читаю книгу Modern Concurrency с raywenderlich.com и предполагаю, что книга устарела или что-то в этом роде, я пытаюсь запустить closure внутри AsyncStream, но, похоже, это не получается, я все еще хорош новичок в этом Async/Await, но при добавлении некоторых точек останова я вижу, что мой код туда не попадает. Это мой код и скриншот с некоторыми предупреждениями. Я не совсем знаком с тем, что означают предупреждения, просто пытаюсь изучить все эти новые вещи, я был бы очень признателен за помощь, и есть ли способ исправить это с помощью Swift 6? Заранее спасибо!
Ссылка на захваченную переменную «обратный отсчет» в параллельно выполняющемся коде; это ошибка в Swift 6
Мутация захваченной var 'countdown' в параллельно выполняющемся коде; это ошибка в 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)
}
}





Timer.scheduleTimer требует, чтобы он был запланирован в цикле выполнения. С практической точки зрения это означает, что мы хотели бы запланировать его в цикле выполнения основного потока. Итак, вы либо вызываете scheduleTimer из основного потока, либо создаете Timer и вручную добавляете(_:forMode:) его в RunLoop.main . См. раздел «Планирование таймеров в циклах выполнения» документации Timer.
Проще всего было бы просто возложить эту функцию на главного актера. Например.,
@MainActor
func countdown(to message: String) async throws { … }
Здесь также есть несколько других проблем:
Я бы предложил определить переменную countdown внутри 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)...")
countdown -= 1
}
}
for await countDownMessage in counter {
try await say(countDownMessage)
}
}
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)
}
}
Должно быть замыкание 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)
}
}
Task.sleep — это метод, используемый в обновленной версии книги.
О, парень! Большое спасибо, ваш ответ был действительно полезен, вы дали мне весь контекст, необходимый для понимания того, что происходит и как это решить. Большое спасибо!!
Новая версия книги будет доступна в ближайшее время, это одна из проблем, которые были решены.