'Swift 5.6 how to put async Task into a queue
Let say I have this code
class Duck{
func walk() async {
//do something
print("walk start")
try? await Task.sleep(nanoseconds: UInt64(2e9))
print("walk end")
}
func quack() async {
//do something...
print("quack start")
try? await Task.sleep(nanoseconds: UInt64(2e9))
print("quack end")
}
func fly() async{
//do something
print("fly start")
try? await Task.sleep(nanoseconds: UInt64(2e9))
print("fly end")
}
}
let duck = Duck()
Task{
await duck.walk()
}
Task{
await duck.quack()
}
Task{
await duck.fly()
}
This would print
walk start
quack start
fly start
walk end
quack end
fly end
which I understand and expected. But what if I want those 3 Tasks run sequentially? Let say each Task is created by user pressing a button. I want the tasks to queue up in the background and run one by one. Is there any thing like you can queue up DispatchWorkItem in a DispatchQueue, but a Task version?
Edit:
I came up with a solution, but I am not sure if this is a good way to implement it. As this implementation potentially create many layer of cascaded Task, I wonder if there would be risk of stack overflow or memory leaks?
class TaskQueue{
private var currentTask : Task<Void,Never> = Task{}
func dispatch(block:@escaping () async ->Void){
let oldTask = currentTask
currentTask = Task{
_ = await oldTask.value
await block()
}
}
}
taskQueue.dispatch {
await duck.walk()
}
taskQueue.dispatch {
await duck.quack()
}
taskQueue.dispatch {
await duck.fly()
}
Solution 1:[1]
Here is my updated implementation which I think is safer than the one I posted in the question. The TaskQueueActor part does all of the job, I wrap it with an outer class just to make it cleaner when calling from a non-async context.
class TaskQueue{
private actor TaskQueueActor{
private var blocks : [() async -> Void] = []
private var currentTask : Task<Void,Never>? = nil
func addBlock(block:@escaping () async -> Void){
blocks.append(block)
next()
}
func next()
{
if(currentTask != nil) {
return
}
if(!blocks.isEmpty)
{
let block = blocks.removeFirst()
currentTask = Task{
await block()
currentTask = nil
next()
}
}
}
}
private let taskQueueActor = TaskQueueActor()
func dispatch(block:@escaping () async ->Void){
Task{
await taskQueueActor.addBlock(block: block)
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ricky Mo |
