top | item 44014004

(no title)

mst | 9 months ago

Oh my word.

    const defer = f => ({ [Symbol.dispose]: f })

    using defer(() => cleanup())
That only just occurred to me. To everybody else who finds it completely obvious, "well done" but it seemed worthy of mention nonetheless.

discuss

order

masklinn|9 months ago

Note that depending on use case, it may be preferrable to use `DisposableStack` and `AsyncDisposableStack` which are part of the `using` proposal and have built-in support for callback registration.

This is notably necessary for scope-bridging and conditional registration as `using` is block-scoped so

    if (condition) {
        using x = { [Symbol.dispose]: cleanup }
    } // cleanup is called here
But because `using` is a variant of `const` which requires an initialisation value which it registers immediately this will fail:

    using x; // SyntaxError: using missing initialiser
    if (condition) {
        x = { [Symbol.dispose]: cleanup };
    }
and so will this:

    using x = { [Symbol.dispose]() {} };
    if (condition) {
        // TypeError: assignment to using variable
        x = { [Symbol.dispose]: cleanup }
    }
Instead, you'd write:

    using x = new DisposableStack;
    if (condition) {
        x.defer(cleanup)
    }
Similarly if you want to acquire a resource in a block (conditionally or not) but want the cleanup to happen at the function level, you'd create a stack at the function toplevel then add your disposables or callbacks to it as you go.

MrResearcher|9 months ago

What is the purpose of DisposableStack.move()? Can it be used to transfer the collected .defer() callbacks entirely out of the current scope, e.g. up the call stack? Probably would be easier to pass DisposableStack as an argument to stack all .defer() callbacks in the caller's context?