Any tips on context manager similar to Python in Javascript?

Viewed 2128

I quite liked Python's context manager where I did not have to be concerned with how the resource was obtained or cleaned up afterwards. Is there a way to do this in a neat way in Javascript?

3 Answers

You can make something similar using generator functions in javascript:

function* usingGroup() {
   // setup
   console.group()
   try {
     yield
   } finally {
     // cleanup
     console.groupEnd()
   }
}

and to use it, you can use a for ... of loop:

for(const _ of usingGroup()) {
   console.log('inside a group')
}

This method is similar to how you define context managers in python using the @contextmanager decorator. Although the way of using a for loop to define where the context applies feels strange in my opinion.

How does it work:

Abstracting away the whole generator object and iterator protocol, you can think of it as doing this: before the first iteration of the loop, the code inside the generator function is run until the yield statement, at this point, the for loop body runs once since a value has been yielded by the generator, before the next iteration, the rest of the code in the generator is run. Since it does not yield, the loop considers it done and exits without executing its body again. The try ... finally ensures that even if an error is thrown, the code gets executed.

If you need to use some value inside your loop (like a file you opened for example), you can just yield it inside the generator.

full code:

function* usingGroup() {
   console.group()
   try {
     yield
   } finally {
     console.groupEnd()
   }
}

for(const _ of usingGroup()) {
   console.log('inside a group')
}

I'm happy to break this to you, we now have that in JavaScript. It's a tiny library called "contextlib" (closely resembling python's contexlib).

Disclaimer: I authored contextlib.

You can install using npm install contextlib.

import {With, contextmanager} from 'contextlib';

class context:
    enter() { console.log('entering context') }

    exit() { console.log('leaving context') }

With(new context(), () => {
   console.log('inside context')
})

You can also use the contextmanager decorator

context = contextmanager(function*(){
    try {
        console.log('entering context')
        yield
    } finally {
        console.log('leaving context')
    }
})

You should check it out on github.

Related