import Foundation /// Serializes refreshes so bursts of events do not start overlapping fetches. /// Calls arriving during a pass wait for a trailing pass that starts after them. /// Use one runner per operation: trailing passes reuse the first caller's closure. /// Cancelling a waiter does not cancel the shared work. Do not call this runner /// recursively from its pass, since that would wait for itself. @MainActor public final class CoalescedRunner { private var current: Task? private var queued = false public init() {} public func run(_ pass: @escaping @MainActor () async -> Void) async { if let current { queued = true await current.value return } let task = Task { @MainActor [weak self] in repeat { self?.queued = false await pass() } while self?.queued == true // No suspension after clearing: a later caller must never attach // to completed work and return without a fresh pass. self?.current = nil } current = task await task.value } }