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
}
