So that the read lock is lifted even if reader.read() throws an error.
Does this only hold for long running processes? In a browser environment or in a cli script that terminates when an error is thrown, would the lock be lifted when the process exits?
creata · 14m ago
This seems error-prone, for at least two reasons:
* If you accidentally use `let` or `const` instead of `using`, everything will work but silently leak resources.
* Objects that contain resources need to manually define `dispose` and call it on their children. Forgetting to do so will lead to resource leaks.
It looks like defer dressed up to resemble RAII.
qprofyeh · 1h ago
Can someone explain why they didn’t go with (anonymous) class destructors? Or something other than a Symbol as special object key. Especially when there are two Symbols (different one for asynchronous) which makes it a leaky abstraction, no?
matharmin · 1h ago
Destructors I other languages are typically used for when the object is garbage collected. That has a whole bunch of associated issues, which is why the pattern is often avoided these days.
The dispose methods on the other hand are called when the variable goes out of scope, which is much more predictable. You can rely on for example a file being closed ot a lock released before your method returns.
JavaScript is already explicit about what is synchronous versus asynchronous everywhere else, and this is no exception. Your method needs to wait for disposing to complete, so if disposing is asynchronous, your method must be asynchronous as well. It does get a bit annoying though that you end up with a double await, as in `await using a = await b()` if you're not used to that syntax.
As for using symbols - that's the same as other functionality added over time, such as iterator. It gives a nice way for the support to be added in a backwards-compatible way. And it's mostly only library authors dealing with the symbols - a typical app developer never has to touch it directly.
senfiaj · 16m ago
For garbage collected languages destructors cannot be called synchronously in most cases because the VM must make sure that the object is inaccessible first. So it will not work very deterministically, and also will expose the JS VM internals. For that JS already has WeakRef and FinalizationRegistry.
Need to dig into this more, but I built OneJS [1] (kinda like React Native but for Unity), and at first glance this looks perfect for us(?). Seems to be super handy for Unity where you've got meshes, RenderTextures, ComputeBuffers, and NativeContainers allocations that all need proper disposal outside of JS. Forcing disposal at lexical scopes, we can probs keep memory more stable during long Editor sessions or when hot-reloading a lot.
Remind me of C#.. IDisposible and IAsyncDisposible in C# helps a lot to write good mechanisms for things that should actually be abstracted in a nice way (such as locks handling, queue mechanisms, temporary scopes for impersonation, etc).
bvrmn · 51m ago
Context managers: exist.
JS: drop but we couldn't occupy a possibly taken name, Symbol for the win!
It's hilariously awkward.
demarq · 26m ago
nah, Symbol has been traits for javascript for quite a while eg. Symbol.iterator
It's the "dispose" part where the new name is decided.
roschdal · 7m ago
JavaScript new features: segmentation faults, memory leaks, memory corruption and core dumps.
russellbeattie · 45m ago
Maybe it's just me, but [Symbol.dispose]() seems like a really hacky way to add that functionality to an Object. Here's their example:
First, I had to refresh my memory on the new object definition shorthand: In short, you can use a variable or expression to define a key name by using brackets, like: let key = "foo"; { [key]: "bar"}, and secondly you don't have to write { "baz" : function(p) { ... } }, you can instead write { baz(p) {...} }. OK, got it.
So, if I'm looking at the above example correctly, they're implementing what is essentially an Interface-based definition of a new "resource" object. (If it walks like a duck, and quacks...)
To make a "resource", you'll tack on a new magical method to your POJO, identified not with a standard name (like Object.constructor() or Object.__proto__), but with a name that is a result of whatever "Symbol.dispose" evaluates to. Thus the above definition of { [Symbol.dispose]() {...} }, which apparently the "using" keyword will call when the object goes out of scope.
Do I understand that all correctly?
I'd think the proper JavaScript way to do this would be to either make a new object specific modifier keyword like the way getters and setters work, or to create a new global object named "Resource" which has the needed method prototypes that can be overwritten.
Using Symbol is just weird. Disposing a resource has nothing to do with Symbol's core purpose of creating unique identifiers. Plus it looks fugly and is definitely confusing.
Is there another example of an arbitrary method name being called by a keyword? It's not a function parameter like async/await uses to return a Promise, it's just a random method tacked on to an Object using a Symbol to define the name of it. Weird!
Maybe I'm missing something.
demarq · 22m ago
Yes you are missing something. You are not supposed to call these methods, they are for the runtime.
more specifically, javascript will call the [Symbol.dispose] when it detects you are exiting the scope of a "using" declaration.
paavohtl · 27m ago
JS has used "well-known symbols"[1] to allow extending / overriding the functionality of objects for about 10 years. For example, an object is an iterable if it has a `[Symbol.iterator]` property. Symbols are valid object keys; they are not just string aliases.
Does this only hold for long running processes? In a browser environment or in a cli script that terminates when an error is thrown, would the lock be lifted when the process exits?
* If you accidentally use `let` or `const` instead of `using`, everything will work but silently leak resources.
* Objects that contain resources need to manually define `dispose` and call it on their children. Forgetting to do so will lead to resource leaks.
It looks like defer dressed up to resemble RAII.
The dispose methods on the other hand are called when the variable goes out of scope, which is much more predictable. You can rely on for example a file being closed ot a lock released before your method returns.
JavaScript is already explicit about what is synchronous versus asynchronous everywhere else, and this is no exception. Your method needs to wait for disposing to complete, so if disposing is asynchronous, your method must be asynchronous as well. It does get a bit annoying though that you end up with a double await, as in `await using a = await b()` if you're not used to that syntax.
As for using symbols - that's the same as other functionality added over time, such as iterator. It gives a nice way for the support to be added in a backwards-compatible way. And it's mostly only library authors dealing with the symbols - a typical app developer never has to touch it directly.
https://waspdev.com/articles/2025-04-09/features-that-every-... https://waspdev.com/articles/2025-04-09/features-that-every-...
But even Mozilla doesn't recommend to use it because it's quite unpredictable and might work differently in different engines.
[1] https://github.com/Singtaa/OneJS
JS: drop but we couldn't occupy a possibly taken name, Symbol for the win!
It's hilariously awkward.
It's the "dispose" part where the new name is decided.
So, if I'm looking at the above example correctly, they're implementing what is essentially an Interface-based definition of a new "resource" object. (If it walks like a duck, and quacks...)
To make a "resource", you'll tack on a new magical method to your POJO, identified not with a standard name (like Object.constructor() or Object.__proto__), but with a name that is a result of whatever "Symbol.dispose" evaluates to. Thus the above definition of { [Symbol.dispose]() {...} }, which apparently the "using" keyword will call when the object goes out of scope.
Do I understand that all correctly?
I'd think the proper JavaScript way to do this would be to either make a new object specific modifier keyword like the way getters and setters work, or to create a new global object named "Resource" which has the needed method prototypes that can be overwritten.
Using Symbol is just weird. Disposing a resource has nothing to do with Symbol's core purpose of creating unique identifiers. Plus it looks fugly and is definitely confusing.
Is there another example of an arbitrary method name being called by a keyword? It's not a function parameter like async/await uses to return a Promise, it's just a random method tacked on to an Object using a Symbol to define the name of it. Weird!
Maybe I'm missing something.
more specifically, javascript will call the [Symbol.dispose] when it detects you are exiting the scope of a "using" declaration.
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
those methods could conflict with existing methods already used in other ways if you’d want to make an existing class a subclass of Resource.
https://news.ycombinator.com/item?id=44012969