Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a difference between "@MainActor in" and "MainActor.run"?

Is there any difference between:

Task { await MainActor.run { ... } }

and

Task { @MainActor in ... }
like image 896
Philip Pegden Avatar asked Sep 08 '25 16:09

Philip Pegden


1 Answers

One difference is that one takes a synchronous closure whereas the other uses an async closure. Specifically, run takes a synchronous closure (i.e., the body is not async):

public static func run<T>(resultType: T.Type = T.self, body: @MainActor @Sendable () throws -> T) async rethrows -> T where T : Sendable

This is ideally suited for the scenario where you are on some other actor, but want to run a series of three methods, all of which are isolated to the main actor, but want to do it with a single context switch, not three.

But, in Task.init, the operation is async, which makes it a slightly more flexible mechanism:

public init(priority: TaskPriority? = nil, operation: @escaping @Sendable () async -> Success)

So, to illustrate the difference, consider:

Task { @MainActor in
    statusText = "Fetching"
    await viewModel.fetchData()
    statusText = "Done"
}

But you cannot await within MainActor.run:

Task {
    await MainActor.run {            // Cannot pass function of type '@Sendable () async -> ()' to parameter expecting synchronous function type
        statusText = "Fetching"
        await viewModel.fetchData()
        statusText = "Done"
    }
}

You would have to insert yet another Task inside. (!)

Task {
    await MainActor.run {
        Task {
            statusText = "Fetching"
            await viewModel.fetchData()
            statusText = "Done"
        }
    }
}

I actually use both patterns sparingly, but this is one difference between them.

like image 198
Rob Avatar answered Sep 10 '25 14:09

Rob