# Async support in defer blocks?

**URL:** <https://forums.swift.org/t/async-support-in-defer-blocks/69455>\
**Category:** Discussion\
**Tags:** concurrency\
**Created:** [January 15, 2024, 6:38pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455 "2024-01-15T18:38:07Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![bjhomer](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bjhomer/32/313_2.png) [@bjhomer](https://forums.swift.org/u/bjhomer)\
**Post date:** [January 15, 2024, 6:38pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/1 "2024-01-15T18:38:07Z")

</div>

Currently, it is not possible to `await` an async call within a defer block. Are there reasons that could not be allowed (given an appropriate evolution proposal, etc.)?

Example of what I want to do:

```swift
func runOperation() async {
  await disableIdleTimer()
  defer { await enableIdleTimer() }

  // Implement the actual operation here
}

```

The idle timer I'm dealing with is `MainActor`-isolated, but the operation here is designed to run in the background. So I do need to `await` these calls.

I can, of course, just move the code inside `defer {}` down to the bottom of the operation. But using a defer makes it easier to see the pairing between the calls, and also ensure that I don't miss it via an early return somewhere in the operation code.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [January 16, 2024, 6:00am UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/2 "2024-01-16T06:00:42Z")

</div>

We've been considering adding this feature.

The only concern about this was the implicit suspensions this adds at the end of the function -- but this is the same as an `async let` so this already exists in the language.

It'd need an evolution proposal and implementation, but it is likely we'd like to do this eventually.

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [January 16, 2024, 6:00am UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/3 "2024-01-16T06:00:52Z")

</div>

It's a known limitation, e.g. [prior lamentations](https://forums.swift.org/t/async-rethrows-and-defer/58356).

You can wrap your `await enableIdleTimer()` in `Task { … }`, as a workaround. Or at least, you can if you don't care if `enableIdleTimer` concludes before `runOperation`.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [January 16, 2024, 6:03am UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/4 "2024-01-16T06:03:15Z")

</div>

I don't recommend wrapping in `Task{}` as that dramatically changes semantics and guarantees -- the cleanup will not be guaranteed to complete before the function returns which most certainly is wrong for such enable/disable swapping.

For now you'll have to write the boilerplate at return points, OR write a function like

```swift

await withCleanup { 
  await disableIdleTimer() 
  // logic
} cleanup: {
  await enableIdleTimer() 
}

```

or make your code closure based entirely "`withDisabledIdleTimer() { logic }`" or similar.

---

<div class="post-metadata">

**Author:** ![FranzBusch](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/franzbusch/32/19447_2.png) [@FranzBusch](https://forums.swift.org/u/FranzBusch)\
**Post date:** [January 16, 2024, 7:48am UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/5 "2024-01-16T07:48:10Z")

</div>

When async defer is brought up people often want cancellation shields as well to write clean up logic that is protected from cancellation. A common example is creating a file descriptor and wanting to close it in an async defer block. This async defer must run otherwise you would leak the descriptor.

Overall, I would love to see both problems solved in the language since we have come across a need for them often.

---

<div class="post-metadata">

**Author:** ![0xTim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/0xtim/32/27968_2.png) [@0xTim](https://forums.swift.org/u/0xTim)\
**Post date:** [January 16, 2024, 11:07am UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/6 "2024-01-16T11:07:28Z")

</div>

Big +1 to this. It's a real sore point at the moment for anyone trying to do async stuff and having to rely on manual reviews to ensure each potential return point of a function does the async cleanup manually is very un-swiftlike

---

<div class="post-metadata">

**Author:** ![jeremyabannister](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jeremyabannister/32/10143_2.png) [@jeremyabannister](https://forums.swift.org/u/jeremyabannister)\
**Post date:** [January 16, 2024, 11:37am UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/7 "2024-01-16T11:37:12Z")

</div>

> [@FranzBusch](#):
>
> When async defer is brought up people often want cancellation shields as well

In [SE-0415: Function Body Macros](https://forums.swift.org/t/se-0415-function-body-macros/68847) there was discussion of allowing defer blocks to access the returned value or the thrown error (e.g. [this comment of mine](https://forums.swift.org/t/se-0415-function-body-macros/68847/26)). I would be happy to see this added to the language.

---

<div class="post-metadata">

**Author:** ![kabiroberai](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/kabiroberai/32/28072_2.png) [@kabiroberai](https://forums.swift.org/u/kabiroberai)\
**Post date:** [January 16, 2024, 9:12pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/8 "2024-01-16T21:12:33Z")

</div>

Speaking of `async let`, you can in fact leverage it to (hackily) replicate an async `defer`:

```swift
public func deferAsync(_ perform: @escaping @Sendable () async -> Void) async {
    // suspends until cancelled
    for await _ in AsyncStream<Never>.makeStream().stream {}
    await Task { await perform() }.value
}

func example() async {
    async let _ = deferAsync {
        print("Starting deferred")
        try? await Task.sleep(for: .seconds(1))
        print("Completed deferred")
    }

    print("Starting primary")
    try? await Task.sleep(for: .seconds(1))
    print("Completed primary")

    // == output ==
    // Starting primary
    // <sleeps 1 sec>
    // Completed primary
    // Starting deferred
    // <sleeps 1 sec>
    // Completed deferred
}

```

In this snippet, the `async let _` is cancelled when `example` is about to go out of scope, which causes the closure to execute. This does mean the closure is executed in the "cancelled" state so it's necessary to spawn a new unstructured task to "un-cancel" the body. That said this approach has quite a few limitations such as 1) not being able to prove to the compiler that the closure runs at the end instead of in parallel with other code, and 2) not being able to control the order of execution of multiple `deferAsync` blocks. Indeed, a bona fide `defer async` would be great to have.

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [January 16, 2024, 9:18pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/9 "2024-01-16T21:18:59Z")

</div>

What ensures the deferred closure is concluded before `example` returns?

---

<div class="post-metadata">

**Author:** ![kabiroberai](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/kabiroberai/32/28072_2.png) [@kabiroberai](https://forums.swift.org/u/kabiroberai)\
**Post date:** [January 16, 2024, 9:38pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/10 "2024-01-16T21:38:08Z")

</div>

That’s guaranteed by the fact that `async let` is structured. There’s also an unstructured task being spun up here but that’s only for the un-cancellation trick; and the unstructured task’s value is await-ed by `deferAsync` so it’s also guaranteed to return before the function returns.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [January 8, 2025, 8:37pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/11 "2025-01-08T20:37:20Z")

</div>

Every now and then I run into bugs which would be very most naturally fixed by a `defer` but which instead need different workarounds because the cleanup is `async`. Would love to see a proposal for this come to review!

---

<div class="post-metadata">

**Author:** ![SlugFiller](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slugfiller/32/33973_2.png) [@SlugFiller](https://forums.swift.org/u/SlugFiller)\
**Post date:** [January 8, 2025, 9:20pm UTC](https://forums.swift.org/t/async-support-in-defer-blocks/69455/12 "2025-01-08T21:20:11Z")

</div>

Gonna shamelessly plug my [pitch](https://forums.swift.org/t/pitch-syncconsumable/74310) for async deinit, which could achieve the same, at the cost of a wrapper class (Or better yet, a `~Copyable` struct).

IMO, structured concurrency is generally better presented using destructors than scopes. Like, suppose if instead of `async let`, you simply had a `StructuredTask` which would inherit cancellation when awaited, and cancel and await in its destructor. In addition to doing everything `async let` does, it would also:

- Allow returning it from a function, so the caller can `await` it instead. Currently, this requires a different pattern where you return a `Task` or closure and the caller `async let`s it.
- Allow having it as a member of another class. It cancels and waits when the class deinits
- Allow adding it to an array. This has the same effect as adding a task to a `TaskGroup`, but without the with-style scoping. When the `Array` goes out of scope, the task is cancelled and awaited. An extra parameter to make it not auto-cancel could farther make the same behavior as a `TaskGroup` (wait all instead of cancel by default). Or just wrapping it in another class that would do it in its `deinit`

But yeah, `await` inside `deinit` is something you kind of expect to Just Work™.
