Invoke concurrent functions sequentially

Hi,

Overview

  • I have a function f1 which is non-async function.
  • f1 gets called multiple times and I have no control over the calling of f1
  • When f1 gets called I would like to invoke an async function f2

Aim:

  • I would like f2 to complete before the next f2 executes

Question:

  • How can I ensure f2 executes in sequence? (sample code below)
//I have no control over f1
//f1 can get called multiple times in quick succession.
func f1() {
    Task {
        try await f2() // Next time f1 gets called it should wait for the previous f2 to complete then should execute f2
    }
}

func f2() async throws {}

Two suggestions:

  1. Use an actor. If f2 is an actor method, then it will be invoked serially (with respect to other methods and itself on the same actor).

or

  1. Save a reference to the task and then await the result:
var task: Task<Void, Error>?

func f1() {
    let previous = task
    task = Task {
        _ = await previous?.result
        try await f2()
    }
}
3 Likes

Thanks a lot @jhammer, that was perfect I was thinking of option 2 but didn't know how to do it. Thanks !!!

I had a similar problem and your second suggestion helped me get a much cleaner version, thank you!

In my case I also do not want to use actor because it prevents inheritance, and I've the added constraint of f1() being async. To my understanding this means that with your sample code, task member could get assigned a task by two distinct threads at the same time (or some tasks may be lost). So I adapted it as follow:

private let queue = DispatchQueue(label: "SomeQueue")
private var taskInProgress: Task<URL?, Never>?
func f1() async -> URL? {
    let task = queue.sync { () -> Task<URL?, Never> in
        let previousTask = taskInProgress
        let newTask = Task { () -> URL? in
            _ = await previousTask?.result
            return await f2()
        }
        
        taskInProgress = newTask
        return newTask
    }
    
    return try? await task.result.get()
}
1 Like