Last active
January 11, 2026 19:35
-
-
Save robertmryan/a186546becfdca50ce45eaf3098e441d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // The three child functions can presumably fail for a variety of reasons: | |
| // | |
| // 1. The request of data from the cloud failed. | |
| // 2. The saving of the data locally failed. | |
| // 3. The function was canceled. | |
| // | |
| // And for each of these, you want to let the parent task know why it failed. | |
| // So, we would generally mark these child functions as `throws`. | |
| // | |
| // And we would explicitly avoid ever using `try?` or `try!` in these child | |
| // functions. And we’d avoid a `catch` block (to allow errors to percolate up | |
| // to the caller that will update the UI), or if the child function did need | |
| // a `catch` block, we’d also have that block re-`throw` the error. | |
| // | |
| // We would also avoid introducing any unnecessary unstructured | |
| // concurrency (e.g., `Task {…}` or `Task.detached {…}`) in these child | |
| // functions. As an aside, in the unlikely case that unstructured concurrency, | |
| // was needed, we would wrap that in a `withTaskCancellationHandler`, but that | |
| // is beyond the scope of the question. | |
| func updateDrumsFromTheCloud() async throws {…} | |
| func updateMixFromTheCloud() async throws {…} | |
| func updateClapsFromTheCloud() async throws {…} | |
| // Anyway, we can then just `try await` these calls, with no explicit | |
| // `Task.isCancelled` checks: | |
| func updateEverythingFromTheCloud() async throws { | |
| try await updateDrumsFromTheCloud() // these take a minute | |
| try await updateMixFromTheCloud() | |
| try await updateClapsFromTheCloud() | |
| } | |
| // And then the caller might, for example, create the `Task` for this async | |
| // work, and then update the UI accordingly: | |
| var updateTask: Task<Void, Never>? | |
| updateTask = Task { | |
| do { | |
| try await updateEverythingFromTheCloud() | |
| await refreshUI() | |
| } catch { | |
| await updateUI(for: error) | |
| } | |
| } |
Author
Author
By the way, if you want advice on how to make these three child functions handle cancellation properly, maybe you can share an example implementation of be of them, and we can advise further.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In the above, note that cancelling
updateTaskwill propagate that to the three child functions automatically because we have remained within the realm of structured concurrency. But also note that no explicit checks ofTask.isCancelled(or better,try Task.checkCancellation()) are needed.As an aside, on the basis of these function names, it does not appear that the results of one call are dependent upon the other. So we might let these run concurrently:
Like the original example above, cancelling
updateTaskfor this implementation will also automatically propagate cancellation to these three child functions.