Home > Enterprise >  Async/await equivalent of saving a completion block of a method
Async/await equivalent of saving a completion block of a method

Time:09-17

I have a function in a NetworkManager class which loads an object Bar and returns it in a completion block. If my app is in the background, I save the completion block in a class FooManager like this:

func getBar(
  withRequestId requestId: String,
  completion: (Bar) -> Void
) {
  let applicationState = UIApplication.shared.applicationState
  if applicationState == .background {
    let request = BarRequest(
      id: requestId,
      completion: completion
    )
    fooManager.request = request
    return
  }
  ...
}

Then, in FooManager when the applicationDidBecomeActive notification is received, I call the function again and pass in the completion handler, such that the original request can complete and return back to the caller:

func applicationDidBecomeActive() {
  guard let request else {
    return
  }
  network.getBar(
    withRequestId: request.id,
    completion: request.completion
  )
  self.request = nil
}

This works well. However, I'm now converting this function to async but I can't figure out how to do the equivalent of this without a completion block to save.

func getBar(withRequestId requestId: String,) async throws -> Config {
  let applicationState = UIApplication.shared.applicationState
  if applicationState == .background {
    let configRequest = ConfigRequest(
      id: requestId,
      completion: completion // Can't do this now
    )
    configManager.configRequest = configRequest
    return
  }
  ...
}

How can I do this, or something that's equivalent to this?

CodePudding user response:

I gather that getBar sometimes does nothing with the closure, but squirrels it away for future use, and sometimes presumably calls it. If that is the case, that simply is not going to refactor nicely to an async function.

The contract with async functions is that they will always either return the value or throw (though, given that your closure did not have an error parameter to the closure, it seems that this should not be a function that throws). Besides, the code that runs after the async method is done is not passed as a parameter, but rather is back in the caller’s “continuation”, that section of code after the await “suspension” point. You cannot save this continuation in a property, to call later.

So, if you really need this “save this block of code so I can call it later” pattern, you might want to just stay with the closure parameter. Just because you have a method that has a closure parameter does not mean that you have to make it async. The point of async-await is to capture clear dependencies between asynchronous tasks, but if you need to save a block of code to call at some other point in time, a closure might be the best pattern.

  • Related