Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 5.5 concurrency: Are scheduled Tasks serial to one another

i saw this thread Swift 5.5 Concurrency: how to serialize async Tasks to replace an OperationQueue with maxConcurrentOperationCount = 1? but i am not clear on whether two scheduled Tasks will execute serially. What i mean is, if i had a piece of code

func fetchImages() {
   Task.init {
     let fetch = await loadImages()
   }

   Task.init {
     let fetch1 = await loadImages1()
   }
}

will first task always finish before second task starts? The application i am trying to get is to execute task 1 as soon as possible (save time) but task 2 relies on the result of task 1 so it needs to wait for task 1 to finish before proceeding. Task 2 also is only conditionally triggered upon an event so they cannot be in the same Task.

like image 328
infoMining Avatar asked Nov 18 '25 13:11

infoMining


1 Answers

You asked:

will first task always finish before second task starts?

No, it will not. Whenever you see await, that is a “suspension point” at which Swift concurrency is free to switch to another task. In short, these can run concurrently. Let me illustrate that with Xcode Instruments:

import os.log

private let poi = OSSignposter(subsystem: "Test", category: .pointsOfInterest)

class Foo {
    func fetchImages() {
        Task {
            let fetch = await loadImages()
            print("done loadImages")
        }
        
        Task {
            let fetch1 = await loadImages1()
            print("done loadImages1")
        }
    }
    
    func loadImages() async {
        // start “points of interest” interval
        let state = poi.beginInterval(#function, id: poi.makeSignpostID(), "start")
        
        // await some 3-second asynchronous task
        try? await Task.sleep(for: .seconds(3))

        // end “points of interest” interval
        poi.endInterval(#function, state, "end")
    }

    func loadImages1() async {
        // start “points of interest” interval
        let state = poi.beginInterval(#function, id: poi.makeSignpostID(), "start")
    
        // await some 1-second asynchronous task
        try? await Task.sleep(for: .seconds(3))

        // end “points of interest” interval
        poi.endInterval(#function, state, "end")
    }
}

Profiling (in Xcode, either press command-i or choose from the menu, “Product” » “Profile”) this with the “time profiler” in Instruments, you can see that these run concurrently:

enter image description here


The trick is to have the second task await the first one. E.g.,

func fetchImages() {
    let firstTask = Task {
        let fetch = await loadImages()
        print("done loadImages")
    }
    
    Task {
        _ = await firstTask.result
        let fetch1 = await loadImages1()
        print("done loadImages1")
    }
}

Or you can store the first task in some property:

var firstTask: Task<Void, Never>?   // you may need to adjust the `Success` and `Failure` types to match your real example

func fetchImages() {
    firstTask = Task {
        let fetch = await loadImages()
        print("done loadImages")
    }
    
    Task {
        _ = await firstTask?.result
        let fetch1 = await loadImages1()
        print("done loadImages1")
    }
}

When you do that, you can see the sequential execution:

enter image description here

FWIW, this concept, of using await on the prior task was the motivating idea behind that other answer that you referenced. That is a more generalized rendition of the above. Hopefully this illustrates the mechanism outlined in that other answer.

like image 129
Rob Avatar answered Nov 21 '25 10:11

Rob



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!