Search code examples
swiftasync-awaitconcurrencytimeout

Swift: Have a timeout for async/await function


Is there a way to attach a timeout to stop an async function if it takes too long?

Here's a simplified version of my code:

func search() async -> ResultEnum {
  // Call multiple Apis one after the other
}

Task.init {
  let searchResult = await search() 
  switch searchResult {
    // Handle all result scenarios
  }
}

I would like to have a deadline for the search() async function to provide a result, otherwise it should terminate and return ResultEnum.timeout.


Solution

  • Thank you Rob for your comments, and for the link you provided.

    I had to make some changes though, For some reason the initial task fetchTask kept going even after cancellation, until I added Task.checkCancellation() to it.

    Here's what the code looks like now, if anyone is facing a similar issue:

    func search() async throws -> ResultEnum {
      // This is the existing method as per my initial question.
      // It calls multiple Apis one after the other, then returns a result.
    }
    
    // Added the below method to introduce a deadline for search()
    func search(withTimeoutSecs: Int) async {
        let fetchTask = Task {
            let taskResult = try await search()
            try Task.checkCancellation()
            // without the above line, search() kept going until server responded long after deadline.
            return taskResult
        }
            
        let timeoutTask = Task {
            try await Task.sleep(nanoseconds: UInt64(withTimeoutSecs) * NSEC_PER_SEC)
            fetchTask.cancel()
        }
            
        do {
            let result = try await fetchTask.value
            timeoutTask.cancel()
            return result
        } catch {
            return ResultEnum.failed(NetworkError.timeout)
        }
    }
    

    // Call site: Using the function (withTimeout:) instead of ()
    Task.init {
        let searchResult = await search(withTimeoutSecs: 6) 
        switch searchResult {
          // Handle all result scenarios
        }
    }