import Foundation
class MyOperationQueue {
static let shared = MyOperationQueue()
private var queue: OperationQueue
init() {
self.queue = OperationQueue()
queue.name = "com.myqueue.name"
queue.maxConcurrentOperationCount = 1
queue.qualityOfService = .background
}
func requestDataOperation() {
queue.addOperation {
print("START NETWORK \(Date())")
NetworkService.shared.getData()
print("END NETWORK \(Date())")
}
}
func scheduleSleep() {
queue.cancelAllOperations()
queue.addOperation {
print("SLEEP START \(Date())")
Thread.sleep(forTimeInterval: 5)
print("SLEEP END \(Date())")
}
}
func cancelAll() {
queue.cancelAllOperations()
}
}
I put requestDataOperation
function inside a timer for every 10 seconds interval. And I have a button to call scheduleSleep
manually. I was expected to debounce the request for every 5 more seconds when I tapping the button.
But I am getting something like this:
START NETWORK
END NETWORK
SLEEP START 2021-03-11 11:13:40 +0000
SLEEP END 2021-03-11 11:13:45 +0000
SLEEP START 2021-03-11 11:13:45 +0000
SLEEP END 2021-03-11 11:13:50 +0000
START NETWORK
END NETWORK
How to add 5 more seconds since my last tapping and combine it together rather than split it into two operation? I call queue.cancelAllOperations
and start a new sleep operation but doesn't seem to work.
Expect result:
START NETWORK
END NETWORK
SLEEP START 2021-03-11 11:13:40 +0000
// <- the second tap when 2 seconds passed away
SLEEP END 2021-03-11 11:13:47 +0000 // 2+5
START NETWORK
END NETWORK
If you want some operation to be delayed for a certain amount of time, I would not create a “queue” class, but rather I would just define an
Operation
that simply will not beisReady
until that time has passed (e.g., five seconds later). That eliminates the need for separate “sleep operations”.E.g.,
And I eliminate races on
timer
andblock
with this property wrapper:Anyway, having defined that
DelayedOperation
, then you can do something likeAnd it will delay running that task (in this case, just logging “some task” message) for five seconds. If you want to reset the timer, just call that method on the operation subclass:
For example, here I created the task, added it to the queue, reset it three times at two second intervals, and the block actually runs five seconds after the last reset:
Now, if you're using operations for network requests, then you are presumably already implemented your own asynchronous
Operation
subclass that does the necessary KVO forisFinished
,isExecuting
, etc., so you may choose marry the aboveisReady
logic with that existingOperation
subclass.But the idea is one can completely lose the "sleep" operation with an asynchronous pattern. If you did want a dedicate sleep operation, you could still use the above pattern (but make it an asynchronous operation rather than blocking a thread with
sleep
).All of this having been said, if I personally wanted to debounce a network request, I would not integrate this into the operation or operation queue. I would just do that debouncing at the time that I started the request: