Movatterモバイル変換


[0]ホーム

URL:


  1. Web
  2. JavaScript
  3. Reference
  4. Statements and declarations
  5. using

using

Limited availability

This feature is not Baseline because it does not work in some of the most widely-used browsers.

Theusing declaration declares block-scoped local variables that aresynchronously disposed. Likeconst, variables declared withusing must be initialized and cannot be reassigned. The variable's value must be eithernull,undefined, or an object with a[Symbol.dispose]() method. When the variable goes out of scope, the[Symbol.dispose]() method of the object is called, to ensure that resources are freed.

Syntax

js
using name1 = value1;using name1 = value1, name2 = value2;using name1 = value1, name2 = value2, /* …, */ nameN = valueN;
nameN

The name of the variable to declare. Each must be a legal JavaScriptidentifier andnot adestructuring binding pattern.

valueN

Initial value of the variable. It can be any legal expression but its value must be eithernull,undefined, or an object with a[Symbol.dispose]() method.

Description

This declaration can be used:

Most notably, it cannot be used:

  • At the top level of a script, because script scopes are persistent.
  • At the top level of aswitch statement.
  • In the initializer of afor...in loop. Because the loop variable can only be a string or symbol, this doesn't make sense.

Ausing declares a disposable resource that's tied to the lifetime of the variable's scope (block, function, module, etc.). When the scope exits, the resource is disposed of synchronously. The variable is allowed to have valuenull orundefined, so the resource can be optionally present.

When the variable is first declared and its value is non-nullish, adisposer is retrieved from the object. If the[Symbol.dispose] property doesn't contain a function, aTypeError is thrown. This disposer is saved to the scope.

When the variable goes out of scope, the disposer is called. If the scope contains multipleusing orawait using declarations, all disposers are run in the reverse order of declaration, regardless of the type of declaration. All disposers are guaranteed to run (much like thefinally block intry...catch...finally). All errors thrown during disposal, including the initial error that caused the scope exit (if applicable), are all aggregated inside oneSuppressedError, with each earlier exception as thesuppressed property and the later exception as theerror property. ThisSuppressedError is thrown after disposal is complete.

using ties resource management to lexical scopes, which is both convenient and sometimes confusing. There are many ways to preserve the variable's value when the variable itself is out of scope, so you may hold a reference to an already-disposed resource. See below for some examples where it may not behave how you expect. If you want to hand-manage resource disposal, while maintaining the same error handling guarantees, you can useDisposableStack instead.

Examples

In the following examples, we assume a simpleResource class that has agetValue method and a[Symbol.dispose]() method:

js
class Resource {  value = Math.random();  #isDisposed = false;  getValue() {    if (this.#isDisposed) {      throw new Error("Resource is disposed");    }    return this.value;  }  [Symbol.dispose]() {    this.#isDisposed = true;    console.log("Resource disposed");  }}

using in a block

The resource declared withusing is disposed when exiting the block.

js
{  using resource = new Resource();  console.log(resource.getValue());  // resource disposed here}

using in a function

You can useusing in a function body. In this case, the resource is disposed when the function finishes executing, immediately before the function returns.

js
function example() {  using resource = new Resource();  return resource.getValue();}

Here,resource[Symbol.dispose]() will be called aftergetValue(), before thereturn statement executes.

The resource may outlive the declaration, in case it's captured by aclosure:

js
function example() {  using resource = new Resource();  return () => resource.getValue();}

In this case, if you callexample()(), you will always executegetValue on a resource that's already disposed, because the resource was disposed whenexample returns. In case you want to dispose the resource immediately after the callback has been called once, consider this pattern:

js
function example() {  const resource = new Resource();  return () => {    using resource2 = resource;    return resource2.getValue();  };}

Here, wealias aconst-declared resource to ausing-declared resource, so that the resource is only disposed after the callback is called; note that if it is never called then the resource will never be cleaned up.

using in a module

You can useusing at the top level of a module. In this case, the resource is disposed when the module finishes executing.

js
using resource = new Resource();export const value = resource.getValue();// resource disposed here

export using is invalid syntax, but you canexport a variable declared elsewhere usingusing:

js
using resource = new Resource();export { resource };

This is still discouraged, because the importer will always receive a disposed resource. Similar to the closure problem, this causes the value of resource to live longer than the variable.

using withfor...of

You can useusing in the initializer of afor...of loop. In this case, the resource is disposed on every loop iteration.

js
const resources = [new Resource(), new Resource(), new Resource()];for (using resource of resources) {  console.log(resource.getValue());  // resource disposed here}

Multipleusing

The following are two equivalent ways to declare multiple disposable resources:

js
using resource1 = new Resource(),  resource2 = new Resource();// ORusing resource1 = new Resource();using resource2 = new Resource();

In both cases, when the scope exits,resource2 is disposed beforeresource1. This is becauseresource2 may have a dependency onresource1, so it's disposed first to ensure thatresource1 is still available whenresource2 is disposed.

Optionalusing

using allows the variable to have valuenull orundefined, so the resource can be optionally present. This means you don't have to do this:

js
function acquireResource() {  // Imagine some real-world relevant condition here,  // such as whether there's space to allocate for this resource  if (Math.random() < 0.5) {    return null;  }  return new Resource();}const maybeResource = acquireResource();if (maybeResource) {  using resource = maybeResource;  console.log(resource.getValue());} else {  console.log(undefined);}

But can do this:

js
using resource = acquireResource();console.log(resource?.getValue());

using declaration without using the variable

You can achieve automatic resource disposing usingusing, without even using the variable. This is very useful for setting up a context within a block, such as creating a lock:

js
{  using _ = new Lock();  // Perform concurrent operations here  // Lock disposed (released) here}

Note that_ is a normal identifier, but it's a convention to use it as a "throwaway" variable. To create multiple unused variables, you need to use distinct names, for example by using a variable name prefixed with_.

Initialization and temporal dead zones

using variables are subject to the sametemporal dead zone restriction aslet andconst variables. This means that you can't access the variable before the initialization—the valid lifetime of the resource is strictly from its initialization to the end of its scope. This is enablesRAII-style resource management.

js
let useResource;{  useResource = () => resource.getValue();  useResource(); // Error: Cannot access 'resource' before initialization  using resource = new Resource();  useResource(); // Valid}useResource(); // Error: Resource is disposed

Error handling

Theusing declaration is the most useful for managing resource disposal in the presence of errors. If you are not careful, some resources may leak because the error prevents code afterwards from executing.

js
function handleResource(resource) {  if (resource.getValue() > 0.5) {    throw new Error("Resource value too high");  }}try {  using resource = new Resource();  handleResource(resource);} catch (e) {  console.error(e);}

This will successfully catch the error thrown byhandleResource and log it, and no matter ifhandleResource throws an error or not, the resource is disposed before exiting thetry block.

Here, if you don't useusing, you may do something like:

js
try {  const resource = new Resource();  handleResource(resource);  resource[Symbol.dispose]();} catch (e) {  console.error(e);}

But, ifhandleResource() throws an error, then control never reachesresource[Symbol.dispose](), and the resource is leaked. Furthermore, if you have two resources, then errors thrown in earlier disposals may prevent later disposals from running, leading to more leaks.

Consider a more complicated case where the disposer itself throws an error:

js
class CantDisposeMe {  #name;  constructor(name) {    this.#name = name;  }  [Symbol.dispose]() {    throw new Error(`Can't dispose ${this.#name}`);  }}let error;try {  using resource1 = new CantDisposeMe("resource1");  using resource2 = new CantDisposeMe("resource2");  throw new Error("Error in main block");} catch (e) {  error = e;}

You can inspect the error thrown in your browser's console. It has the following structure:

SuppressedError: An error was suppressed during disposal  suppressed: SuppressedError: An error was suppressed during disposal    suppressed: Error: Can't dispose resource1    error: Error: Error in main block  error: Error: Can't dispose resource2

As you can see,error contains all the errors that were thrown during disposal, as aSuppressedError. Each additional error is added as theerror property, and the original error is added as thesuppressed property.

Specifications

Specification
ECMAScript Async Explicit Resource Management
# prod-UsingDeclaration

Browser compatibility

See also

Help improve MDN

Learn how to contribute

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp