1

I want to debounce a batch of events and process them after a delay of ~1.5 seconds. Here's what I've done.

class ViewModel: ObservableObject { @Published var pending: [TaskInfo] private var cancellable: AnyCancellable? = nil init() { processPendingTasks() } func queueTask(task: TaskInfo) { pending.append(task) } private func processPendingTasks() { cancellable = $pendingTasks .debounce(for: 1.5, scheduler: RunLoop.main) .sink(receiveValue: { batch in // Iterate though elements and process events. }) } } 

Issue: This works fine, but the issue that I've is that it performs unnecessary view updates since the array is tagged @Published.

What I'm looking for: The ideal approach would be a streaming setup where I get all events (in a batched fashion) but the sink should wait exactly for 1.5 seconds after the last event was added.

I tried PassthroughSubject, but it seems like it only gets me the last event that happened in the last 1.5 seconds.

1 Answer 1

1

A possible solution is a combination of a PassthroughSubject and the collect operator. In queueTask send the tasks to the subject.

func queueTask(task: TaskInfo) { subject.send(task) } 

1.5 seconds after receiving the last item send

subject.send(completion: .finished) 

and subscribe

subject .collect() .sink { [weak self] tasks in self?.pending = tasks } 

If the interval of the incoming tasks is < 1.5 seconds you could also use the .timeout(1.5) operator which terminates the pipeline after the timeout interval.

Sign up to request clarification or add additional context in comments.

4 Comments

This is a nice solution. I tried it and I've only one issue now. Since I'm calling .finished, the sink is only called once. What I want is to continue the stream even after the sink is called, i.e. after one batch is finished processing - it should start over again listening for a new batch.
As you wrote after the last event was added I assumed that the transmission was finished after the last item. Alternatively you could use collect(n) which emits the items in chunks of n.
Yeah I ended up using the .collect(.byTime) pattern with a delay of 1.5. It works for my use case although not exactly what I asked in the question.
collect() was the missing piece that I was looking for. Thank you for that!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.