1
- import Combine
1
+ @ preconcurrency import Combine
2
2
import Dispatch
3
3
import Foundation
4
4
5
- extension Effect {
5
+ extension Effect where Action : Sendable {
6
6
/// Throttles an effect so that it only publishes one output per given interval.
7
7
///
8
8
/// The throttling of an effect is with respect to actions being sent into the store. So, if
@@ -23,12 +23,13 @@ extension Effect {
23
23
/// `false`, the publisher emits the first element received during the interval.
24
24
/// - Returns: An effect that emits either the most-recent or first element received during the
25
25
/// specified interval.
26
- public func throttle< S: Scheduler > (
26
+ public func throttle< S: Scheduler & Sendable > (
27
27
id: some Hashable & Sendable ,
28
28
for interval: S . SchedulerTimeType . Stride ,
29
29
scheduler: S ,
30
30
latest: Bool
31
- ) -> Self {
31
+ ) -> Self
32
+ where S. SchedulerTimeType. Stride: Sendable {
32
33
switch self . operation {
33
34
case . none:
34
35
return . none
@@ -42,45 +43,44 @@ extension Effect {
42
43
publisher
43
44
. receive ( on: scheduler)
44
45
. flatMap { value -> AnyPublisher < Action , Never > in
45
- throttleLock. lock ( )
46
- defer { throttleLock. unlock ( ) }
46
+ throttleState. withValue {
47
+ guard let throttleTime = $0. times [ id] as! S . SchedulerTimeType ? else {
48
+ $0. times [ id] = scheduler. now
49
+ $0. values [ id] = nil
50
+ return Just ( value) . eraseToAnyPublisher ( )
51
+ }
47
52
48
- guard let throttleTime = throttleTimes [ id] as! S . SchedulerTimeType ? else {
49
- throttleTimes [ id] = scheduler. now
50
- throttleValues [ id] = nil
51
- return Just ( value) . eraseToAnyPublisher ( )
52
- }
53
-
54
- let value = latest ? value : ( throttleValues [ id] as! Action ? ?? value)
55
- throttleValues [ id] = value
53
+ let value = latest ? value : ( $0. values [ id] as! Action ? ?? value)
54
+ $0. values [ id] = value
56
55
57
- guard throttleTime. distance ( to: scheduler. now) < interval else {
58
- throttleTimes [ id] = scheduler. now
59
- throttleValues [ id] = nil
60
- return Just ( value) . eraseToAnyPublisher ( )
61
- }
56
+ guard throttleTime. distance ( to: scheduler. now) < interval else {
57
+ $0 . times [ id] = scheduler. now
58
+ $0 . values [ id] = nil
59
+ return Just ( value) . eraseToAnyPublisher ( )
60
+ }
62
61
63
- return Just ( value)
64
- . delay (
65
- for: scheduler. now. distance ( to: throttleTime. advanced ( by: interval) ) ,
66
- scheduler: scheduler
67
- )
68
- . handleEvents (
69
- receiveOutput: { _ in
70
- throttleLock. sync {
71
- throttleTimes [ id] = scheduler. now
72
- throttleValues [ id] = nil
62
+ return Just ( value)
63
+ . delay (
64
+ for: scheduler. now. distance ( to: throttleTime. advanced ( by: interval) ) ,
65
+ scheduler: scheduler
66
+ )
67
+ . handleEvents (
68
+ receiveOutput: { _ in
69
+ throttleState. withValue {
70
+ $0. times [ id] = scheduler. now
71
+ $0. values [ id] = nil
72
+ }
73
73
}
74
- }
75
- )
76
- . eraseToAnyPublisher ( )
74
+ )
75
+ . eraseToAnyPublisher ( )
76
+ }
77
77
}
78
78
}
79
79
. cancellable ( id: id, cancelInFlight: true )
80
80
}
81
81
}
82
82
}
83
83
84
- var throttleTimes : [ AnyHashable : Any ] = [ : ]
85
- var throttleValues : [ AnyHashable : Any ] = [ : ]
86
- let throttleLock = NSRecursiveLock ( )
84
+ private let throttleState = LockIsolated < ( times : [ AnyHashable : Any ] , values : [ AnyHashable : Any ] ) > (
85
+ ( times : [ : ] , values : [ : ] )
86
+ )
0 commit comments