Skip to content

Commit d432276

Browse files
committed
Optimize the logic of canceling timers
1 parent 6e590e9 commit d432276

File tree

1 file changed

+25
-11
lines changed

1 file changed

+25
-11
lines changed

Sources/AsyncTimer/AsyncTimer.swift

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,26 @@ public final actor AsyncTimer {
4848
/// This handler is called when the timer is cancelled.
4949
private var cancelHandler: CancelHandler?
5050

51-
/// Initializes a new `AsyncRepeatingTimer` instance.
51+
/// Whether the timer is running.
52+
public var isRunning: Bool { task != nil }
53+
54+
/// Initializes a new `AsyncTimer` instance.
5255
/// - Parameters:
5356
/// - interval: The interval at which the timer fires.
5457
/// - priority: The priority of the task. Default is `.medium`.
5558
/// - repeating: Whether the timer should repeat. Default is `false`.
5659
/// - firesImmediately: Whether the timer should fire immediately upon starting. Default is `true`. It is only effective when `repeating` is `true`.
5760
/// - handler: The handler that is called when the timer fires.
5861
/// - cancelHandler: The handler that is called when the timer is cancelled.
59-
/// - Returns: A new `AsyncRepeatingTimer` instance.
62+
/// - Returns: A new `AsyncTimer` instance.
6063
public init(interval: TimeInterval,
6164
priority: TaskPriority = .medium,
6265
repeating: Bool = false,
6366
firesImmediately: Bool = true,
6467
handler: @escaping RepeatHandler,
6568
cancelHandler: CancelHandler? = nil)
6669
{
70+
precondition(interval > 0, "Interval must be greater than 0")
6771
self.interval = interval
6872
self.priority = priority
6973
self.firesImmediately = firesImmediately
@@ -79,24 +83,32 @@ public final actor AsyncTimer {
7983
task = Task(priority: priority) {
8084
guard repeating else {
8185
// one-time timer
82-
try await Self.sleep(interval)
83-
await self.handler()
86+
do {
87+
try await Self.sleep(interval)
88+
await self.handler()
89+
} catch is CancellationError {
90+
// timer was cancelled
91+
await cancelHandler?()
92+
}
8493
return
8594
}
8695

8796
// repeating timer
88-
if !firesImmediately {
89-
try await Self.sleep(interval)
90-
}
9197
do {
98+
if !firesImmediately {
99+
try await Self.sleep(interval)
100+
}
92101
while !Task.isCancelled {
93102
await self.handler()
103+
if Task.isCancelled { break }
94104
try await Self.sleep(interval)
95105
}
96106
} catch is CancellationError {
97-
await cancelHandler?()
98-
return
99-
} catch {}
107+
// timer was cancelled
108+
} catch {
109+
// unexpected error
110+
}
111+
await cancelHandler?()
100112
}
101113
}
102114

@@ -117,8 +129,10 @@ public final actor AsyncTimer {
117129
/// - Parameter newInterval: The new interval at which the timer should fire.
118130
/// - Note: This will also restart the timer.
119131
public func setInterval(_ newInterval: TimeInterval) {
132+
precondition(newInterval > 0, "Interval must be greater than 0")
133+
guard interval != newInterval else { return }
120134
interval = newInterval
121-
restart()
135+
if isRunning { restart() }
122136
}
123137
}
124138

0 commit comments

Comments
 (0)