Skip to content

Commit

Permalink
Iterator disposal
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh-Cena committed Feb 8, 2025
1 parent 4cbe720 commit 20b3c31
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 6 deletions.
46 changes: 43 additions & 3 deletions files/en-us/web/javascript/guide/resource_management/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Some things to note:

## The `DisposableStack` and `AsyncDisposableStack` objects

`using` and `await using` are special syntaxes. Syntaxes are convenient and hides a lot of the complexity, but sometimes you need to do things manually.
`using` and `await using` are special syntaxes. Syntaxes are convenient and hide a lot of the complexity, but sometimes you need to do things manually.

For one common example: what if you don't want to dispose the resource at the end of _this_ scope, but at some _later_ scope? Consider this:

Expand All @@ -197,9 +197,49 @@ if (someCondition) {

However, this means all logic has to be written inside the `if` or `else`, causing a lot of duplication. What we want to do is to acquire and register the resource in one scope but dispose it in another. We can use a {{jsxref("DisposableStack")}} for this purpose.

Another use case is when you have a resource that does not yet implement the disposable protocol, so it will be rejected by `using`. Yet another use case is when you have a disposal action to perform but it's not "tethered" to any resource in particular. Maybe you just want to log a message saying "All database connections closed" when there are multiple connections open simultaneously.
```js
{
using stack = new DisposableStack();
let reader;
if (someCondition) {
reader = stack.use(stream.getReader());
} else {
reader = stack.use(stream.getReader({ mode: "byob" }));
}
// Do something with reader
// Before scope exit, stack is disposed, which disposes reader
}
```

TODO
Another use case is when you have a resource that does not yet implement the disposable protocol, so it will be rejected by `using`.

```js
{
using stack = new DisposableStack();
// Suppose reader does not have the [Symbol.dispose]() method,
// then it cannot be used with using.
// However, we can manually pass a disposer function to stack.adopt
const reader = stack.adopt(stream.getReader(), (reader) =>
reader.releaseLock(),
);
// Do something with reader
// Before scope exit, stack is disposed, which disposes reader
}
```

Yet another use case is when you have a disposal action to perform but it's not "tethered" to any resource in particular. Maybe you just want to log a message saying "All database connections closed" when there are multiple connections open simultaneously.

```js
{
using stack = new DisposableStack();
stack.defer(() => console.log("All database connections closed"));
const connection1 = stack.use(openConnection());
const connection2 = stack.use(openConnection());
// Do something with connection1 and connection2
// Before scope exit, stack is disposed, which first disposes connection1
// and connection2 and then logs the message
}
```

## Error handling

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,31 @@ None ({{jsxref("undefined")}}).

## Examples

### Disposing an async iterator
### Declaring an async iterator with `await using`

The `Symbol.asyncDispose` method is intended to be automatically called in an `await using` declaration. This is useful if you have an async iterator that you manually iterate over by calling its `next()` method; if you iterate it with {{jsxref("Statements/for-await...of", "for await...of")}} or something similar, then error handling and cleanup is done automatically.

```js
async function* generateNumbers() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log("Cleaning up");
}
}

async function doSomething() {
await using numbers = generateNumbers();
const res1 = await numbers.next();
// Not iterating the rest of the numbers
// Before the function exists, the async iterator is disposed
// Logs "Cleaning up"
}

doSomething();
```

## Specifications

Expand All @@ -36,3 +60,7 @@ None ({{jsxref("undefined")}}).
{{Compat}}

## See also

- [JavaScript resource management](/en-US/docs/Web/JavaScript/Guide/Resource_management)
- {{jsxref("Symbol.asyncDispose")}}
- {{jsxref("Statements/await_using", "await using")}}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ browser-compat: javascript.builtins.Iterator.@@dispose

{{JSRef}}

The **`[Symbol.dispose]()`** method of {{jsxref("Iterator")}} instances implements the _disposable protocol_ and allows it to be disposed when used with {{jsxref("Statements/using", "using")}} or {{jsxref("Statements/await_using", "await using")}}. It calls the `return()` method of `this`, if it exists.
The **`[Symbol.dispose]()`** method of {{jsxref("Iterator")}} instances implements the _disposable protocol_ and allows it to be disposed when used with {{jsxref("Statements/using", "using")}}. It calls the `return()` method of `this`, if it exists.

## Syntax

Expand All @@ -25,7 +25,31 @@ None ({{jsxref("undefined")}}).

## Examples

### Disposing an iterator
### Declaring an iterator with `using`

The `Symbol.dispose` method is intended to be automatically called in a `using` declaration. This is useful if you have an iterator that you manually iterate over by calling its `next()` method; if you iterate it with {{jsxref("Statements/for...of", "for...of")}} or something similar, then error handling and cleanup is done automatically.

```js
function* generateNumbers() {
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log("Cleaning up");
}
}

function doSomething() {
using numbers = generateNumbers();
const res1 = numbers.next();
// Not iterating the rest of the numbers
// Before the function exists, the async iterator is disposed
// Logs "Cleaning up"
}

doSomething();
```

## Specifications

Expand All @@ -36,3 +60,7 @@ None ({{jsxref("undefined")}}).
{{Compat}}

## See also

- [JavaScript resource management](/en-US/docs/Web/JavaScript/Guide/Resource_management)
- {{jsxref("Symbol.dispose")}}
- {{jsxref("Statements/using", "using")}}
2 changes: 2 additions & 0 deletions files/sidebars/jssidebar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ sidebar:
title: Guide_Typed_arrays
- link: /Web/JavaScript/Guide/Iterators_and_generators
title: Guide_Iterators_generators
- link: /Web/JavaScript/Guide/Resource_management
title: Guide_Resource_management
- link: /Web/JavaScript/Guide/Internationalization
title: Guide_Internationalization
- link: /Web/JavaScript/Guide/Meta_programming
Expand Down

0 comments on commit 20b3c31

Please sign in to comment.