I know that await does not directly spawn a new thread, but just tell the function to suspend so the system can utilize threads for other work. But when it comes to how / which thread the system will use to continue executing the awaited task / work is a bit unclear.
I'm testing this on Xcode 14, where I have 3 fetch versions. Upon running here are my observations
- ViewModel.fetch1: run on main thread
- ViewModel.fetch2: run on cooperative thread pool
- ContentView.fetch: run on main thread
class ViewModel: ObservableObject {
@Published var string = ""
func fetch1() {
let url = URL(string: "https://google.com")!
let data = try! Data(contentsOf: url)
self.string = String(data: data, encoding: .utf8) ?? ""
}
func fetch2() async {
let url = URL(string: "https://google.com")!
let data = try! Data(contentsOf: url)
self.string = String(data: data, encoding: .utf8) ?? ""
}
}
struct ContentView: View {
@State var string = ""
@StateObject var vm = ViewModel()
var body: some View {
VStack {
Button {
Task {
await vm.fetch1()
}
Task {
await vm.fetch2()
}
Task {
await fetch()
}
} label: {
Text("Fetch")
}
Text(string)
Text(vm.string)
}
}
private func fetch() async {
let url = URL(string: "https://google.com")!
let data = try! Data(contentsOf: url)
self.string = String(data: data, encoding: .utf8) ?? ""
}
}
I know that using new async/await concurrency model, it's not wise to think about thread, but here I'm trying to make sure it does not run heavy work on the main thread.
For the ContentView.fetch
and ViewModel.fetch1
methods being executed on the main thread, it's understandable that Task inherits the actor context, and since I make unstructured Task
in SwiftUI body which is using main actor.
For the ViewModel.fetch2
method, I mark it as async
, and somehow Swift decides to use cooperative thread pool instead of "inheriting" main actor context?