Posts

Post not yet marked as solved
5 Replies
0 Views
For the first part of this thread, the goal was just more efficient code. The assumption is that this: func outerWrapper<S>(arg1: Any, arg2: String = "ignore me") throws -> AsyncTask<S> { return theAsyncFunc(arg1: arg1, arg2: arg2) } is more efficient than this: func outerWrapper<S>(arg1: Any, arg2: String = "ignore me") async throws -> S { return await theAsyncFunc(arg1: arg1, arg2: arg2) } Negligibly so, sure. But I actually care about the efficiency of my code. It also makes for fewer edits in the migration to concurrency. If the former is not allowed in swift, so be it. Not a big deal. Was just curious and wanted to advocate. For the second side note, the overall Task is run in a method of an ObservableObject, which serves as one of my view models and which is triggered by some user interaction with SwiftUI. The added complication is that the class inherits some functionality from NSObject and conforms to a protocol that is common to most of the models in my project. So: class MyViewModel: NSObject, LoadingProtocol, SomeObjcDelegate { @Published var loading: Bool = false ..... func businessLogic() { loading = true Task { do { let result = try await someAsyncFunc(....) await MainActor.run { self.swiftUIStateVar = result.someValue } } catch { await MainActor.run { self.errorToDisplay = error } } finally { await MainActor.run { self.loading = false } } } } } And I agree that doing everything in the main actor is ideal, which is what I kind of assumed would be the case. And, if I'm not mistaken, I would be able to add the @MainActor modifier to the class definition to make it so... except for the inheritance. And the modifier prevented the class from conforming to the protocol. The await someAsyncFunc(....) is ultimately async because it makes a URLSession dataTask call. At run time, when it returned, I got a purple warning/error about the code not running on the main actor, and the UI didn't update. As written above, it seems to work. But I posted just because I'm still new to the api and maybe there's a better way given the constraints of my situation. And my critique, then, with my limited perspective, is that it should be main actor by default, and the declaration I would like to add would mark whatever can be pushed off to other actors: something like await method() on someClassOfActor. Arguably, each method in the call stack from someAsyncFunc down to just before the actual dataTask is quick enough to run on the main actor. And every step processing the returned data back up the call stack is also probably quick enough. So that, at least in my case, main actor by default without extra decorators would be sufficient. PromiseKit, the package from which I'm migrating, essentially operates this way, dispatching each callback on the main queue by default unless instructed otherwise.
Post not yet marked as solved
5 Replies
0 Views
Kind of a side note, as I play with this more, it looks like I'm going to do this a lot because my SwiftUI view models currently have an inheritance hierarchy, so that they can't be actors until the whole code base is overhauled. Task { do { let result = try await someAsyncFunc(....) await MainActor.run { self.swiftUIStateVar = result.someValue } } catch { await MainActor.run { self.errorToDisplay = error } } } What am I missing? Is there or can there be a way to run the Task's body on the main thread but send off the awaited call to background threads, like how it would work with asyncio in python?
Post not yet marked as solved
5 Replies
0 Views
@eskimo ....my bad. AsyncTask is a proposal for what I'd like to see if it didn't already exist.. It's just a placeholder for some declaration that would let the compiler know that await outerWrapper() is syntactical ok. I guess I could also write it like: func outerWrapper<S>(arg1: Any, arg2: String = "ignore me") async throws -> S { return theAsyncFunc(arg1: arg1, arg2: arg2) } where it still just avoids using the await keyword. But, in my mind, if the async keyword is used in the function signature, it's an error not to await any asynchronous tasks it calls (which is a common mistake in JS). I'm not really looking to convert the task at all. I'm more looking to skip an await call in a call stack when a given function, outerWrapper in this case, only needs to await once as part of (each of) its return statement(s). In this case, if we can ensure the caller of outerWrapper makes an await call, then the task from theAsyncFunc can propagated up the call stack, and outerWrapper doesn't technically do anything asynchronously in its own function body. Another way I'm thinking about it is returning an async let: func outerWrapper<S>(arg1: Any, arg2: String = "ignore me") -> SomeAsyncTaskThing<S> { async let result = theAsyncFunc(arg1: arg1, arg2: arg2) return result } But it sounds like each function in the call stack must be async and must await all the async functions it calls. I can imagine this makes managing the call stack easier for debug purposes. So I get it.