I have AuthorizationRequester
which can be call from many places simultaneously, but only first call can run requestAuthorizationFromUser()
(and wait dozen of seconds for user interaction) - rest of these calls should await
for result from requestAuthorizationFromUser()
, but can't call it directly.
Look at the code:
actor AuthorizationRequester {
enum SimpleResult {
case success, failure
}
typealias ResultCompletion = (SimpleResult) -> ()
private var requestInProgress = false
private var requestCompletions = [ResultCompletion]()
func request(completion: @escaping ResultCompletion) async {
requestCompletions.append(completion)
guard !requestInProgress else { return }
requestInProgress = true
let result = await requestAuthorizationFromUser()
requestCompletions.forEach { $0(result) }
requestCompletions.removeAll()
requestInProgress = false
}
private func requestAuthorizationFromUser() async -> SimpleResult {
// ...some code
}
}
Everything works, but I really don't like async
combined with completion
closure :)
Is there any possibility to rewrite this function to version with header func request() async -> SimpleResult
and same functionality?
CodePudding user response:
You can save multiple calls to an async
method await
the same Task
associated with the authorization request:
actor AuthorizationRequester {
private var task: Task<SimpleResult, Never>?
func request() async -> SimpleResult {
if task == nil {
task = Task { await requestAuthorizationFromUser() }
}
return await task!.value
}
private func requestAuthorizationFromUser() async -> SimpleResult {
...
}
}
extension AuthorizationRequester {
enum SimpleResult {
case success, failure
}
}
No closures required.
And if you want to reset it again when the Task
finishes (like your sample effectively does), you can set task
to nil
when done:
func request() async -> SimpleResult {
if task == nil {
task = Task { await requestAuthorizationFromUser() }
}
let result = await task!.value
task = nil
return result
}