Skip to content

Commit afbf677

Browse files
authored
Create CancellableQueue (#63)
1 parent 09c6dda commit afbf677

File tree

5 files changed

+801
-2
lines changed

5 files changed

+801
-2
lines changed

AsyncQueue.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'AsyncQueue'
3-
s.version = '0.7.1'
3+
s.version = '0.7.2'
44
s.license = 'MIT'
55
s.summary = 'A queue that enables ordered sending of events from synchronous to asynchronous code.'
66
s.homepage = 'https://github.com/dfed/swift-async-queue'

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,75 @@ func mainActorQueueOrdering() async {
164164
}
165165
```
166166

167+
### Cancelling all executing and pending tasks
168+
169+
Use a `CancellableQueue` to wrap a `FIFOQueue` or `ActorQueue` when you need the ability to cancel all currently executing and pending tasks at once. This is useful for scenarios like cancelling in-flight network requests when a view disappears, or abandoning a batch of operations when a user initiates a new action.
170+
171+
A `CancellableQueue` wraps an underlying queue and tracks all tasks enqueued on it. Calling `cancelTasks()` will cancel both the currently executing task and any tasks waiting in the queue. Tasks that have already completed are unaffected, and tasks enqueued after `cancelTasks()` is called will execute normally.
172+
173+
```swift
174+
@Test
175+
func cancellableQueueExample() async {
176+
actor ImageLoader {
177+
init() {
178+
let actorQueue = ActorQueue<ImageLoader>()
179+
cancellableQueue = CancellableQueue(underlyingQueue: actorQueue)
180+
actorQueue.adoptExecutionContext(of: self)
181+
}
182+
183+
nonisolated
184+
func loadImage(from url: URL) -> Task<UIImage?, Never> {
185+
Task(on: cancellableQueue) { myself in
186+
guard let image = try await myself.fetchImage(from: url) else { return nil }
187+
try Task.checkCancellation()
188+
return myself.processImage(image)
189+
}
190+
}
191+
192+
nonisolated
193+
func cancelAllLoads() {
194+
// Cancels the currently loading image and any queued load requests.
195+
cancellableQueue.cancelTasks()
196+
}
197+
198+
private func fetchImage(from url: URL) async throws -> UIImage? {
199+
// Fetch image implementation…
200+
}
201+
202+
private func processImage(_ image: UIImage) async -> UIImage {
203+
// Expensive image processing implementation…
204+
}
205+
206+
private let cancellableQueue: CancellableQueue<ActorQueue<ImageLoader>>
207+
}
208+
209+
let loader = ImageLoader()
210+
211+
// Enqueue several image load tasks.
212+
let task1 = loader.loadImage(from: URL(string: "https://example.com/1.png")!)
213+
let task2 = loader.loadImage(from: URL(string: "https://example.com/2.png")!)
214+
let task3 = loader.loadImage(from: URL(string: "https://example.com/3.png")!)
215+
216+
// Cancel all pending and executing loads.
217+
loader.cancelAllLoads()
218+
219+
// All tasks are now cancelled.
220+
#expect(task1.isCancelled)
221+
#expect(task2.isCancelled)
222+
#expect(task3.isCancelled)
223+
}
224+
```
225+
226+
A `CancellableQueue` can also wrap a `FIFOQueue` for FIFO-ordered cancellable tasks:
227+
```swift
228+
let cancellableQueue = CancellableQueue(underlyingQueue: FIFOQueue())
229+
230+
Task(on: cancellableQueue) {
231+
// This work can be cancelled via cancellableQueue.cancelTasks()
232+
await performWork()
233+
}
234+
```
235+
167236
## Installation
168237

169238
### Swift Package Manager

0 commit comments

Comments
 (0)