Skip to content

No way to cancel AsyncChannel producer task when consumer task finishes early #304

@fumoboy007

Description

@fumoboy007

Server APIs commonly have a paging mechanism where the client can fetch one page after another. I am implementing a function that returns an AsyncChannel representing a stream of pages from a server. It looks like the following:

func allPages() -> AsyncThrowingChannel<Page, Error> {
   let channel = AsyncThrowingChannel<Page, Error>()

   Task {
      do {
         var currentPage = try await firstPage()

         while true {
            await channel.send(currentPage)

            guard let nextPage = try await currentPage.nextPage() else {
               break
            }
            currentPage = nextPage
         }

         channel.finish()
      } catch {
         channel.fail(error)
      }
   }

   return channel
}

This is a nice abstraction. However, consider the case where the consumer task breaks out of its reader loop early. There is currently no way for the consumer task to indicate to the producer task that it is done with the channel.

@phausler made a related change in #131 to make sure the producer task does not hang when the consumer task is done. However, the producer task still runs to completion, wasting resources.

Instead, I think the send method should throw CancellatonError so that the producer task can handle cancellation gracefully.

(CC @jonathanpenn who wrote a related Swift Forums post.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions