@@ -152,101 +152,149 @@ extension EffectPublisher {
152
152
}
153
153
}
154
154
155
- /// Execute an operation with a cancellation identifier.
156
- ///
157
- /// If the operation is in-flight when `Task.cancel(id:)` is called with the same identifier, the
158
- /// operation will be cancelled.
159
- ///
160
- /// ```
161
- /// enum CancelID.self {}
162
- ///
163
- /// await withTaskCancellation(id: CancelID.self) {
164
- /// // ...
165
- /// }
166
- /// ```
167
- ///
168
- /// ### Debouncing tasks
169
- ///
170
- /// When paired with a clock, this function can be used to debounce a unit of async work by
171
- /// specifying the `cancelInFlight`, which will automatically cancel any in-flight work with the
172
- /// same identifier:
173
- ///
174
- /// ```swift
175
- /// @Dependency(\.continuousClock) var clock
176
- /// enum CancelID {}
177
- ///
178
- /// // ...
179
- ///
180
- /// return .task {
181
- /// await withTaskCancellation(id: CancelID.self, cancelInFlight: true) {
182
- /// try await self.clock.sleep(for: .seconds(0.3))
183
- /// return await .debouncedResponse(
184
- /// TaskResult { try await environment.request() }
185
- /// )
186
- /// }
187
- /// }
188
- /// ```
189
- ///
190
- /// - Parameters:
191
- /// - id: A unique identifier for the operation.
192
- /// - cancelInFlight: Determines if any in-flight operation with the same identifier should be
193
- /// canceled before starting this new one.
194
- /// - operation: An async operation.
195
- /// - Throws: An error thrown by the operation.
196
- /// - Returns: A value produced by operation.
197
- public func withTaskCancellation< T: Sendable > (
198
- id: AnyHashable ,
199
- cancelInFlight: Bool = false ,
200
- operation: @Sendable @escaping ( ) async throws -> T
201
- ) async rethrows -> T {
202
- let id = _CancelToken ( id: id)
203
- let ( cancellable, task) = _cancellablesLock. sync { ( ) -> ( AnyCancellable , Task < T , Error > ) in
204
- if cancelInFlight {
205
- _cancellationCancellables [ id] ? . forEach { $0. cancel ( ) }
155
+ #if swift(>=5.7)
156
+ /// Execute an operation with a cancellation identifier.
157
+ ///
158
+ /// If the operation is in-flight when `Task.cancel(id:)` is called with the same identifier, the
159
+ /// operation will be cancelled.
160
+ ///
161
+ /// ```
162
+ /// enum CancelID.self {}
163
+ ///
164
+ /// await withTaskCancellation(id: CancelID.self) {
165
+ /// // ...
166
+ /// }
167
+ /// ```
168
+ ///
169
+ /// ### Debouncing tasks
170
+ ///
171
+ /// When paired with a clock, this function can be used to debounce a unit of async work by
172
+ /// specifying the `cancelInFlight`, which will automatically cancel any in-flight work with the
173
+ /// same identifier:
174
+ ///
175
+ /// ```swift
176
+ /// @Dependency(\.continuousClock) var clock
177
+ /// enum CancelID {}
178
+ ///
179
+ /// // ...
180
+ ///
181
+ /// return .task {
182
+ /// await withTaskCancellation(id: CancelID.self, cancelInFlight: true) {
183
+ /// try await self.clock.sleep(for: .seconds(0.3))
184
+ /// return await .debouncedResponse(
185
+ /// TaskResult { try await environment.request() }
186
+ /// )
187
+ /// }
188
+ /// }
189
+ /// ```
190
+ ///
191
+ /// - Parameters:
192
+ /// - id: A unique identifier for the operation.
193
+ /// - cancelInFlight: Determines if any in-flight operation with the same identifier should be
194
+ /// canceled before starting this new one.
195
+ /// - operation: An async operation.
196
+ /// - Throws: An error thrown by the operation.
197
+ /// - Returns: A value produced by operation.
198
+ @_unsafeInheritExecutor
199
+ public func withTaskCancellation< T: Sendable > (
200
+ id: AnyHashable ,
201
+ cancelInFlight: Bool = false ,
202
+ operation: @Sendable @escaping ( ) async throws -> T
203
+ ) async rethrows -> T {
204
+ let id = _CancelToken ( id: id)
205
+ let ( cancellable, task) = _cancellablesLock. sync { ( ) -> ( AnyCancellable , Task < T , Error > ) in
206
+ if cancelInFlight {
207
+ _cancellationCancellables [ id] ? . forEach { $0. cancel ( ) }
208
+ }
209
+ let task = Task { try await operation ( ) }
210
+ let cancellable = AnyCancellable { task. cancel ( ) }
211
+ _cancellationCancellables [ id, default: [ ] ] . insert ( cancellable)
212
+ return ( cancellable, task)
206
213
}
207
- let task = Task { try await operation ( ) }
208
- let cancellable = AnyCancellable { task. cancel ( ) }
209
- _cancellationCancellables [ id, default: [ ] ] . insert ( cancellable)
210
- return ( cancellable, task)
211
- }
212
- defer {
213
- _cancellablesLock. sync {
214
- _cancellationCancellables [ id] ? . remove ( cancellable)
215
- if _cancellationCancellables [ id] ? . isEmpty == . some( true ) {
216
- _cancellationCancellables [ id] = nil
214
+ defer {
215
+ _cancellablesLock. sync {
216
+ _cancellationCancellables [ id] ? . remove ( cancellable)
217
+ if _cancellationCancellables [ id] ? . isEmpty == . some( true ) {
218
+ _cancellationCancellables [ id] = nil
219
+ }
217
220
}
218
221
}
222
+ do {
223
+ return try await task. cancellableValue
224
+ } catch {
225
+ return try Result < T , Error > . failure ( error) . _rethrowGet ( )
226
+ }
219
227
}
220
- do {
221
- return try await task. cancellableValue
222
- } catch {
223
- return try Result < T , Error > . failure ( error) . _rethrowGet ( )
228
+ #else
229
+ public func withTaskCancellation< T: Sendable > (
230
+ id: AnyHashable ,
231
+ cancelInFlight: Bool = false ,
232
+ operation: @Sendable @escaping ( ) async throws -> T
233
+ ) async rethrows -> T {
234
+ let id = _CancelToken ( id: id)
235
+ let ( cancellable, task) = _cancellablesLock. sync { ( ) -> ( AnyCancellable , Task < T , Error > ) in
236
+ if cancelInFlight {
237
+ _cancellationCancellables [ id] ? . forEach { $0. cancel ( ) }
238
+ }
239
+ let task = Task { try await operation ( ) }
240
+ let cancellable = AnyCancellable { task. cancel ( ) }
241
+ _cancellationCancellables [ id, default: [ ] ] . insert ( cancellable)
242
+ return ( cancellable, task)
243
+ }
244
+ defer {
245
+ _cancellablesLock. sync {
246
+ _cancellationCancellables [ id] ? . remove ( cancellable)
247
+ if _cancellationCancellables [ id] ? . isEmpty == . some( true ) {
248
+ _cancellationCancellables [ id] = nil
249
+ }
250
+ }
251
+ }
252
+ do {
253
+ return try await task. cancellableValue
254
+ } catch {
255
+ return try Result < T , Error > . failure ( error) . _rethrowGet ( )
256
+ }
224
257
}
225
- }
258
+ #endif
226
259
227
- /// Execute an operation with a cancellation identifier.
228
- ///
229
- /// A convenience for calling ``withTaskCancellation(id:cancelInFlight:operation:)-4dtr6`` with a
230
- /// static type as the operation's unique identifier.
231
- ///
232
- /// - Parameters:
233
- /// - id: A unique type identifying the operation.
234
- /// - cancelInFlight: Determines if any in-flight operation with the same identifier should be
235
- /// canceled before starting this new one.
236
- /// - operation: An async operation.
237
- /// - Throws: An error thrown by the operation.
238
- /// - Returns: A value produced by operation.
239
- public func withTaskCancellation< T: Sendable > (
240
- id: Any . Type ,
241
- cancelInFlight: Bool = false ,
242
- operation: @Sendable @escaping ( ) async throws -> T
243
- ) async rethrows -> T {
244
- try await withTaskCancellation (
245
- id: ObjectIdentifier ( id) ,
246
- cancelInFlight: cancelInFlight,
247
- operation: operation
248
- )
249
- }
260
+ #if swift(>=5.7)
261
+ /// Execute an operation with a cancellation identifier.
262
+ ///
263
+ /// A convenience for calling ``withTaskCancellation(id:cancelInFlight:operation:)-4dtr6`` with a
264
+ /// static type as the operation's unique identifier.
265
+ ///
266
+ /// - Parameters:
267
+ /// - id: A unique type identifying the operation.
268
+ /// - cancelInFlight: Determines if any in-flight operation with the same identifier should be
269
+ /// canceled before starting this new one.
270
+ /// - operation: An async operation.
271
+ /// - Throws: An error thrown by the operation.
272
+ /// - Returns: A value produced by operation.
273
+ @_unsafeInheritExecutor
274
+ public func withTaskCancellation< T: Sendable > (
275
+ id: Any . Type ,
276
+ cancelInFlight: Bool = false ,
277
+ operation: @Sendable @escaping ( ) async throws -> T
278
+ ) async rethrows -> T {
279
+ try await withTaskCancellation (
280
+ id: ObjectIdentifier ( id) ,
281
+ cancelInFlight: cancelInFlight,
282
+ operation: operation
283
+ )
284
+ }
285
+ #else
286
+ public func withTaskCancellation< T: Sendable > (
287
+ id: Any . Type ,
288
+ cancelInFlight: Bool = false ,
289
+ operation: @Sendable @escaping ( ) async throws -> T
290
+ ) async rethrows -> T {
291
+ try await withTaskCancellation (
292
+ id: ObjectIdentifier ( id) ,
293
+ cancelInFlight: cancelInFlight,
294
+ operation: operation
295
+ )
296
+ }
297
+ #endif
250
298
251
299
extension Task where Success == Never , Failure == Never {
252
300
/// Cancel any currently in-flight operation with the given identifier.
0 commit comments