Skip to content

Better Async Effect Cleanup #956

Closed
@rmorshea

Description

@rmorshea

Current Situation

Presently, the "cleanup" behavior for async effects is that they are cancelled before the next effect takes place or the component is unmounted. While this behavior may be desirable in many cases, it does not give the user the ability to gracefully deal with cleanup logic in the way one can with a sync event.

For example, consider a long running async effect:

@use_effect
async def long_running_task():
    while True:
        await something()
        await something_else()

The problem with this current behavior is that handling the cancellation is messy. You could do the following:

@use_effect
async def long_running_task():
    try:
        while True:
            await something()
            await something_else()
    finally:
        ...  # cleanup logic here

However, this may lead to some confusing behavior since it's not possible to know whether something() or something_else() will receive the cancellation. You might have to do a lot of extra work to figure out what state your program was left in after the cancellation.

Proposed Actions

Thankfully the above only impacts async effects. With a synchronous event, you would be able to handle async task cleanup using something like the following:

@use_effect
def start_long_running_task():
    interupt = Event()
    asyncio.create_task(long_running_task(interupt))
    return cleanup.set

async def long_running_task(interrupt):
    while not interupt.is_set():
        await something()
        await something_else()
    ...  # cleanup logic here

Given this, our proposed solution to the problem is to allow async effects to accept a similar interupt asyncio.Event:

@use_effect
async def long_running_task(interrupt):
    while not interupt.is_set():
        await something()
        await something_else()
    ...  # cleanup logic here

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority-2-moderateShould be resolved on a reasonable timeline.release-patchWarrents a patch releasetype-revisionAbout a change in functionality or behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions