Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    2 years ago
    I don’t expect polished documentation at this point, but are there any code comments or anything that would helpful to read to learn about the composition “commit lifecycle” in more detail? E.g.
    launchInComposition
    launches the coroutine on commit, and cancels it on dispose, but it’s not clear if there’s any case in which a coroutine could be launched again in the same point in the composition, after being cancelled. Or whether
    onDispose
    is called if the composition fails before
    onCommit
    gets invoked.
    @Adam Powell mentions avoiding having to “try to rollback” when the composition fails, but if
    onCommit
    will only be called once for a given point in the composition, then as long as
    onDispose
    is called on composition failure it seems like rolling back would be fairly trivial. I feel like I’m missing some piece of the puzzle here. Maybe
    onCommit
    can be called multiple times? https://kotlinlang.slack.com/archives/CJLTWPH7S/p1591565761398100?thread_ts=1591558024.394400&cid=CJLTWPH7S
    Adam Powell

    Adam Powell

    2 years ago
    Good callout, we've been talking about improving a bunch of the docs in this area soon 🙂
    onDispose
    is not called on composition failure, it is called to dispose the result of a previous successful composition
    launchInComposition
    is a sort of task that exists at its position in the composable function; just like a UI element it runs when it becomes present and is cancelled if it leaves the composition before finishing. It does not run again unless you provide it with comparison values that change from one recomposition to another, in which case it will cancel the currently running task block before launching the new one.
    we've considered renaming it to
    Task
    or similar to reinforce this noun-entity behavior of presence in the composition (and joked about calling it
    AsyncTask
    in particular)
    Chuck Jazdzewski [G]

    Chuck Jazdzewski [G]

    2 years ago
    To clarify some underlying semantics not covered by Adam,
    onCommit
    's lambda is only called once the composition succeeds that contains the
    onCommit
    .
    onDispose
    is only called if
    onCommit
    is called and only after composition succeeds that removes the
    onCommit
    from the composition. Calling the
    onCommit
    and
    onDispose
    lambdas is the last step of composition after it has committed to using the results of composition. The quote from Adam above was regarding side-effects in an
    @Composable
    function outside of
    onCommit
    . For example, if you launched a coroutine directly using the main dispatcher in the
    @Composable
    function, this might start a job that is unnecessary and need to be cancelled. However, we currently do not have an API to inform a
    @Composable
    function that its results were discarded. We, therefore, recommend all effects be contained in an
    onCommit
    or similar effect such as
    launchInComposition
    which uses
    onCommit
    internally (well, actually, it uses
    CompositionLifecycleObserver
    which is what
    onCommit
    uses too, but that is an implementation detail).
    v

    Vinay Gaba

    2 years ago
    Just to be clear I understood this correctly, the recommendation is to wrap mutations/side effects inside the onCommit block? So something like this?
    val count by state {0}
    
    onCommit {
     count = count + 1
    }
    I am almost certain I misunderstood the discussion in the previous thread that Zach linked 😅
    Tash

    Tash

    2 years ago
    This discussion is already offering a lot more clarity than the existing code docs so thank y’all. Also curious how
    onPreCommit
    comes into play here, as I see it is used in some internal Compose components.
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    2 years ago
    Thanks a lot for the explanation, Chuck & Adam! Good to know exactly how initializing any kind of resource in a Composable function outside of commit is a potential leak. I am also curious about how
    onPreCommit
    fits into all of this, and a little curious in what scenario, without multi-threading, a composition could fail, but that’s not as interesting as knowing how compose behaves when it happens, so thanks again.