关于类似于 Javascript 中的 Python 的上下文管理器的任何提示?

jim*_*ssy 5 javascript

我非常喜欢 Python 的上下文管理器,在那里我不必关心资源是如何获得或清理的。有没有办法在 Javascript 中以简洁的方式做到这一点?

The*_*tos 14

您可以使用 javascript 中的生成器函数制作类似的东西:

function* usingGroup() {
   // setup
   console.group()
   try {
     yield
   } finally {
     // cleanup
     console.groupEnd()
   }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您可以使用for ... of循环:

for(const _ of usingGroup()) {
   console.log('inside a group')
}
Run Code Online (Sandbox Code Playgroud)

此方法类似于使用@contextmanager装饰器在 python 中定义上下文管理器的方式。尽管在我看来,使用 for 循环来定义上下文应用位置的方式感觉很奇怪。

它是如何工作的:

抽象掉整个生成器对象和迭代器协议,你可以认为它是这样做的:在循环的第一次迭代之前,生成器函数内部的代码运行直到语句yield,此时,for循环体运行一次生成器已经生成了一个值,在下一次迭代之前,生成器中的其余代码将运行。由于它不会产生,因此循环认为它已完成并退出而不再次执行其主体。这try ... finally确保即使抛出错误,代码也会被执行。

如果您需要在循环中使用某些值(例如您打开的文件),您可以将yield其放在生成器中。

完整代码:

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

for(const _ of usingGroup()) {
   console.log('inside a group')
}
Run Code Online (Sandbox Code Playgroud)

编辑:
由于这个答案似乎引起了一些关注,我认为解决几个问题很重要。

首先,这个技巧可能对大多数人来说是陌生的,并且很可能会导致大多数人感到困惑的代码,我想说这不是一个好的代码风格。为了缓解这种情况,您可以使用基于回调的方法来使正在发生的事情更加明确。

另一个问题是,在其状态下,没有任何东西可以阻止、验证或能够真正验证生成器仅产生一次(如果有的话)。这意味着一些脆弱性,并且您不能真正确定 for 循环体只执行一次。这可以在运行时通过包装/装饰上下文管理生成器函数来修复。

最后,当显式资源管理提案被第四阶段接受时,这个答案将在未来几年变得过时。


Dav*_*vvy 8

我很高兴向您透露这一点,我们现在已经在 J​​avaScript 中实现了这一点。这是一个名为“contextlib”的小型库(与 python 的 contexlib 非常相似)。

免责声明:我创作了 contextlib。

您可以使用安装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')
})
Run Code Online (Sandbox Code Playgroud)

您还可以使用contextmanager装饰器

context = contextmanager(function*(){
    try {
        console.log('entering context')
        yield
    } finally {
        console.log('leaving context')
    }
})
Run Code Online (Sandbox Code Playgroud)

你应该在github上查看一下。