The issue is the object that is returned when “using” isn’t a promise and that is the confusing part. This is a deferred action, so await doesn’t make sense here.
While it might seem weird that it requires await, it actually makes sense. Otherwise, you could not be guaranteed that when variable goes out of scope it is actually cleared when function exits. You need async function for that.
A deferred keyword could give the same effect without the need of the await keyword in front of using. This is an internal async call as the object that is assigned by “using” isn’t a Promise, it is bad form to change the expected behavior of real async functions/methods. Adding a new keyword to replace async when the function isn’t what is called during assignment would make this much easier to understand.
@@jfftck but if you call your function that is not async, and it has async dispose handler, it wont be cleaned up at the same event loop tick, its just impossible without async function. myFuncWithUsingKeyword(); console.log('100% guarantee dispose handler is done here'); The above is only possible with sync dispose handler. With async dispose handler, there is no point in code where you can be gauranteed that dispose process was finished. With async func requirement, you at least have a Promise, that when resolved, will make sure that you have disposed it properly.
Nodejs already has implemented the disposal symbols polyfills, and started adding it to many of the existing APIs 🙂 The using keyword isn't natively available yet, but TS and babel have support for it🎉
@@blaaarp4u I would start by cloning the repo, anf building node locally, just to get the feel of how this works, and then just search through the issue tracker for issues you find interest in. As a starting point I recommend doc issues, which are relatively easier, but still allow you to experience the CI & the process involved 🙂
Im using node 20.6 and trying to run this code function openFile() { return { handle: 1, [Symbol.dispose]() { console.log('dispose handle'); } } } function foo() { using f = openFile(); } foo(); but I get error message SyntaxError: Unexpected identifier 'f' what is wrong wit it? or what is wrong with me?
Everything mentioned in the example an be addressed and mitigated by using a try catch with a finally block. With that said, it's great to see that javascript is getting the concept of `using`. It greatly reduces the amount of boilerplate required for handling resources and handles correctly.
the only downside is that try finally is harder to optimize for a browser. I did have issues with performances because of the use of try finally or try catch. It's the only thing I can think of
10:50 Now, I don't know how it's actually proposed and I can't test it now, but my guess is that you can use: *await using a = await openFile("a")* to actually using await as you expected. If that how it is, I think it pretty easy to understand, unless I'm wrong and you need to do some other shenanigans with it.
Thanks for the video, great stuff. Just to add, in any FP language (including js) it'll take literally 5 seconds implementing a higher order function with a scoped variable and a custom dispose behaviour. It obviously wouldn't support all use cases and would be a tiny bit clunkier to have nested code blocks, but it would clearly address the same `loaner pattern` that this `using` thinger does.
Very nice explaination as always. Nice to see JavaScript is catching up on some of the features that other languages have had for years. However I agree with you that the way await is implemented here is strange.
Interesting. For the last 3 months I've been coding in C# and I learned to use the using keyword in C#, I really like it there. It's so weird to see JS become more and more like C#, first with Typescript, and now with this. I like it!
It has 2 meanings. (Edit cuz I accently pressed enter) 1. Use a namespace to not have to write like (e.g. A.Very.Long.Name.Space.Name.Class, A.Very.Long.Name.Space.Name.OtherClass) but just using A.Very.Long.Name.Space.Name; Class... OtherClass... 2. The use that @mikis3300 described
Basically, you create a variable that runs a custom code when it is not in scope anymore, this is really interesting, there are a lot of use-cases for this. The Async Dispose is confusing, I agree
In C# at least, the with statement needs its own scope. Later versions of C# introduced using as well, wich makes the code much more readable, especially in cases when multiple disposable varibles needs to be created.
@@mehdi-vl5nn raii is far less complicated and much more readable than this. and honestly, there are much better methods nowadays, like what python or rust does.
Such a great point about await - that was my gut instinct react: "does the other code need to wait for it now?" I wonder how/if they're going to change this...
Imagine postgres connections.. await using connection = await pg.connect(); Makes more sense to say "async using" or just letting using look for asyncDispose and call it automatically.
I agree with you. I think “using async symbol = expression” would be a better syntax as the symbol is async, it’s not awaiting. And the modifiers (using or async) could be in any order, however that would reduce compiler optimization to be supported. I still think it is a better syntax to accomplish same results. I believe the choice for await is due to parent scope checking, as it must be inside a async scope, and therefore using the word await has the compiler benefit of checking the parent scope to be async, where any other word could make that task a bit harder or even harder to debug as async would check for parent scope only when used along with “using”…
Add my vote to the chorus who observe the obvious inspiration from the Python "with ... as ..." construct. One strength of the Python approach is that the resulting "withContext" is itself a full-blown object with two magic methods -- "__enter__" and "__exit__". This gives the developer the tools needed to reliably handle most situations, while greatly simplifying call sites. One of the strongest motivators for adding the "with ... as ..." construct to Python was the literal impossibility of correctly handling surprisingly common failure scenarios with "try/except/finally" (the Python way of spelling "try/catch/finally"). The bottom line is that "with ... as ..." nearly always works, and is nearly always a better choice than "try/catch/finally". I'm not a language designer or compiler developer, so I can't speak to the constraints that govern the addition of a new keyword to JS. I hope that the JS developers will more fully embrace the learnings from Python. Perhaps something analogous to the "hooks" in React might improve the bare "using" keyword.
Great! It was the first video I have seen talking about "using". Thanks for introducing the thema. Very informative. It looks a good practice when need to dispose objects at the end of its visibility scope. Any ideia when it is going to be supported by major browsers for web development?
You are not alone in having reservations about repurposing *await* in "await using". However, *await* already does different things depending on the context in which it is used, consider "for await…of" statements. I think we should consider *await* to mean: opt into asynchronous behaviour.
History has come full circle. It's like getting 15 years back into .NET framework 2.0 and C# 🙂 Btw, why not use try / finally and close the file in finally block? I have to admit that few years ago I started to dislike the path that JS evolution is going. What next? Destructors?
I dont agree with your last point. If your cleanup is asynchronous, then the setup method is asynchronous as well. Some of the awaiting does happen on the line that has await, if its used correctly
The rapid growth of programming languages in general, and JavaScript in particular, has made me feel overwhelmed. I can't keep up anymore. Clients and employers keep asking us developers to work on one thing today and another the next day. The biggest regret in my career was switching from embedded to frontend development.
You are describing a non-issue in my opinion. This sort of feature is years away from making its way into any large codebase. This is only an issue if you work at a small startup company.
I personally like this because it's the beginning of a magic methods implementation in JS and I really enjoyed the creative freedom those brought in python and php
I agree with you. The await key should be a configuration inside the async disposal instead. When someone else uses my disposable they should not have to know if it's an async or sync disposal. JS should handle that for that developer!
I use a recursive function that reads folders and outputs an object with the folder structure. Wouldn't the dispose just slow down the execution when used inside the recursive function?
Nice video. The syntax with await using is taken one to one from C#, I don't think it's confusing once you understand that await is on using (declaration) not on the assignment.
The syntax may be the same as is used in C#, but is the behavior the same? My understanding is that using the await keyword in C# actually blocks the execution thread while that is not the case in JavaScript. So would the example Kyle used where a log statement is called before the cleanup have the same behavior in C#?
If we're closing multiple asyncs -- does it do the serially, or does it do a promise.all? as to await using -- yeah but ... I sympathize with the challenge of how else to do it better.
Nice video! I think I need to do more research in the asyncDispose. Would node fail if I use async close without asyncDispose? Or would it manage the close of files before exiting the program?
if i want to add return type to the function what should i use? {handle: Type, [Symbol.dispose]: () => any} ?? if so, it's probably better to make a custom Type? type DisposeType = T extends Promise ? {handle: T, [Symbol.asyncDispose]: () => Promise} : {handle: T, [Symbol.dispose]: () => any} 👀
Just like (with-* ...) macros in Common Lisp, they will execute some code after end, even though it has thrown exception: (with-open-file (s "file.txt" :direction :output :if-exists :supersede) (format s "Here are a couple~%of test data lines~%"))
If your close is async, wouldn't your open also generally be async? In the C# version of async/await (the original, I believe), await using opens a new block and the assignment is usually a constructor which isn't async. It makes a little more sense given that you can consider it to be awaiting the completion of the entire block. await using(var f=new File()) { /* do whatever with f */ } Using declarations (without the block open) were added later and behave just like these in js but it's just a shorthand for a block that goes to the end of the scope that the using is in to avoid an indent.
How is this feature complicating things? This allows much more freedom in a compact way. Other languages like C# (using) and Python (with) already have features like this.
Whilst I don't have any immediate uses for 'using' I do like what it does. But I also agree that that extra 'await' is annoying. I feels like it's coming up against the quirky design of async/await which I've fought with before in other sructures.
Is that the "context manager" like in Python? If so, Python has solved it using keyword "with", e. g. with open("my_file.txt", "r") as my_file: ... As for asynchronous context manager async with ... : # some body What is imho prettier, it creates it's own scope (by indentation), unlike this in JS, where it sits in the scope where the variable is created.
Hi Kyle, do you still recommend taking your Free React Hooks course if I have your new React Course. I noticed you go over some of the same hooks, so I'm wondering if it would be redundant to do both now. Thank-you
I love the explanation and looking forward to mainstream adoption, but what are your thoughts on try/catch - finally? I use finally to close DB connections and files after use usually and catch to obviously catch the errors for logs etc. but because finally always runs regardless of the outcome of the try, its a good place to clean up.. no?
Im using node 20.6 and trying to run this code function openFile() { return { handle: 1, [Symbol.dispose]() { console.log('dispose handle'); } } } function foo() { using f = openFile(); } foo(); but I get error message SyntaxError: Unexpected identifier 'f' what is wrong wit it? or what is wrong with me?
shouldnt it be by default that a opened file closes if it gets out of scope? I have the feeling creating another keyword just for something like that seems to complicate things more at the end.
This is a problem with async/await I ran into once in a while, and has not very much to do with the keyword using. There are a lot of examples where await doesn't wait. But you have to check to notice this like you just did. Using sounds great. Could be useful. Especially for libraries. Your example is a bit weird. Guards should always be written at top of the function of course. Return before you do anything. And in javascript most if statement should be false/falsy. If path doesn't include temp write permanent. No return.
I progeamm api in python and clients with angular and typescript. For python it is realy simple to handle stuff like this, with the keayword "with" you can declare a namespace where the file is open and when you leave the namespace got closed, i miss this in javascript and typescript
Well, technically this could be done using finally. But this approach allows cleaner code and less indentations, especially when several such variables are introduced in one function in different places.
It seems like this functionality should be built into the vanilla fs functions. I don't think most coders are going to want to make their own wrappers.
This is cool but I prefer the current way of doing this using a weak reference. Just define a global function like "onCleanup", that you pass an object and a function, then the function you pass gets called when there are no more references to the object and its been garbage collected. Only downside is its restricted to being asynchronous and the object is no longer available after its been cleaned up. You can get around loosing the reference by using a proxy as a wrapper and watching the proxy for garbage collection though, and then your reference in the cleanup code wont prevent it from being GC'ed
Async would make more sense rather than await. Alternatively nothing.. still, nice to have something similar to python’s `with` keyword ( context managers)
Top-level await only works in ESM modules and isn't supported in Node's default CommonJS modules. I'm guessing Kyle just didn't configure his example to use ESM modules, but it would have been valid to use top-level await if he had.
With the way it is implemented, why do we have to make the function call Aynsc and put the await keyword in front of using? That means if the underlying function changes all the callers have to change, yes they might have to do that anyways but this forces it
The await keyword in your demo, is actually making perfect sense. It awaits the "open" method, and the disposal is still handled automatically (which has nothing to do with the open method). So I think everything is fine, and as you would expect coming from another language.
No, look at the openFile function in 4:49. It's not async, what is async is the closing of the file in the Symbol function it returns. It opens the file synchronously but closes it asynchronously.
If the openFile method took 2 seconds to complete, then it would have been more clear. If the openFile is async, execution would not block all scripts, but continue (somewhere else), until the file handle is returned. If the openFile method is synchroneous, all execution would then have been blocked for 2 seconds. It's not illegal to call sync methods within an async execution frame, but you won't benefit. So the placement of the async keyword makes sense. But imagine that he instead of openFile had made an example with a method like OpenHugeFileAndReadAllAsync (which actually took several seconds), it would have been a better example of why you need the async method, and why it makes sense to write it in this way. This would demonstrate that all other async code in the application would have continued to execute, while you at the same time are executing a heavy IO operation.
So in your example, if you call the "openFile" function without defining it's output with "using", then your file will never get closed. You'd have to be sure that either you function documentation is really good, and people actually read it :( Or your developers would always have to look into that function to be sure of what it's doing. I don't see a huge benefit over: try { const file = ...; // Do something with the file. } catch (err) { // Do something useful. } finally { file.closeSync(); }
You don't see the benefit of changing a single word over 7 additional lines which include control flow and additional braces? You already have to do that sort of documentation if you return something that needs to be closed, it just makes is much easier to work into a large variety of flows. Obviously if you have to write the openFile function yourself it isn't great, but presumably libraries like "fs" will include dispose options by default in the future and changing "const" to "using" handles the rest.
@@owenneilb I find that the way that "using" was shown makes the code less obvious. If you have developers who know what they're doing it can be ok, but if you have a lot of junior developers, or just inexperienced developers, then more clear code is less likely to cause issues.
In C I go for a tiny function that sandwiches writeInOpenFile(file) This may seem inconvenient, but it leaves no questions to the reader. I don't do it in Python because the with keyword gives the same guarantee.
The scenario you are providing to explain the new keyword requires you to use an experimental feature with a Pollyfill that makes the example harder not only to test but to understand as well.
This is a feature coming to JS (which is why TS is implementing it). You can see the full proposal here. github.com/tc39/proposal-explicit-resource-management
When you show new features like this one, it would be great if you added some links in the description referencing the specification or feature discussion. I am wondering why it was decided to make it that complicated and not add destructors like we have them in other languages as a complement to the constructors we already have in classes.
This seems like an overly complicated / error prone version of Swift’s defer. It’s more powerful for sure, but that power doesn’t seem necessary. I suppose someone could use this to make a defer function though. You could make the defer function use using internally and call some block when the defer function is out of scope… I think.
The await is now breaking it's promise of waiting for me. **Crying in pain**
The issue is the object that is returned when “using” isn’t a promise and that is the confusing part. This is a deferred action, so await doesn’t make sense here.
😂😂😂
Await is fine here... When you see await next to using, that should be all you need to know. Another keyword is bad... Have you seen C#?
This is basically a mirror of how 'with' working in Python for those who are familiar. Great video!
It is just an implementation of the disposable pattern
As the C#'s "using", Visual Basic "with", the own deprecated "with" in JS, and the list goes on....
it is also similar to the try with resource syntax in java, at least for the autoclose feature.
I think it's closer to C++'s destructors.
While it might seem weird that it requires await, it actually makes sense. Otherwise, you could not be guaranteed that when variable goes out of scope it is actually cleared when function exits. You need async function for that.
A deferred keyword could give the same effect without the need of the await keyword in front of using. This is an internal async call as the object that is assigned by “using” isn’t a Promise, it is bad form to change the expected behavior of real async functions/methods. Adding a new keyword to replace async when the function isn’t what is called during assignment would make this much easier to understand.
@@jfftck but if you call your function that is not async, and it has async dispose handler, it wont be cleaned up at the same event loop tick, its just impossible without async function.
myFuncWithUsingKeyword();
console.log('100% guarantee dispose handler is done here');
The above is only possible with sync dispose handler. With async dispose handler, there is no point in code where you can be gauranteed that dispose process was finished. With async func requirement, you at least have a Promise, that when resolved, will make sure that you have disposed it properly.
I think they should have made it "async using" rather than "await using" since you don't really await here, but you will when you leave the scope
@@4sent4 I agree, it looks like that "await" is purely used to force method to be async, without any other reason.
You are right about the cleanup when the variable is out of scope, but garbage collection is not the same thing.
Nodejs already has implemented the disposal symbols polyfills, and started adding it to many of the existing APIs 🙂
The using keyword isn't natively available yet, but TS and babel have support for it🎉
Thanks! BTW, do you know how I can start contributing to the Node.js project? ? תודה יא מלך! 😅
@@blaaarp4u I would start by cloning the repo, anf building node locally, just to get the feel of how this works, and then just search through the issue tracker for issues you find interest in.
As a starting point I recommend doc issues, which are relatively easier, but still allow you to experience the CI & the process involved 🙂
@@blaaarp4u למד שפה אמיתית, כמו אנגלית
Im using node 20.6 and trying to run this code
function openFile() {
return {
handle: 1,
[Symbol.dispose]() {
console.log('dispose handle');
}
}
}
function foo() {
using f = openFile();
}
foo();
but I get error message SyntaxError: Unexpected identifier 'f'
what is wrong wit it? or what is wrong with me?
@@VidarrKerr
People like you are a disgrace to the open-source community.
Everything mentioned in the example an be addressed and mitigated by using a try catch with a finally block. With that said, it's great to see that javascript is getting the concept of `using`. It greatly reduces the amount of boilerplate required for handling resources and handles correctly.
the only downside is that try finally is harder to optimize for a browser. I did have issues with performances because of the use of try finally or try catch. It's the only thing I can think of
10:50 Now, I don't know how it's actually proposed and I can't test it now, but my guess is that you can use: *await using a = await openFile("a")*
to actually using await as you expected. If that how it is, I think it pretty easy to understand, unless I'm wrong and you need to do some other shenanigans with it.
Thanks for the video, great stuff.
Just to add, in any FP language (including js) it'll take literally 5 seconds implementing a higher order function with a scoped variable and a custom dispose behaviour. It obviously wouldn't support all use cases and would be a tiny bit clunkier to have nested code blocks, but it would clearly address the same `loaner pattern` that this `using` thinger does.
Very nice explaination as always. Nice to see JavaScript is catching up on some of the features that other languages have had for years. However I agree with you that the way await is implemented here is strange.
Interesting. For the last 3 months I've been coding in C# and I learned to use the using keyword in C#, I really like it there. It's so weird to see JS become more and more like C#, first with Typescript, and now with this. I like it!
Isn't using in c# more like import in js?
@@banzai1873nope it's the same as the using in javascript
It has 2 meanings.
(Edit cuz I accently pressed enter)
1. Use a namespace to not have to write like (e.g. A.Very.Long.Name.Space.Name.Class, A.Very.Long.Name.Space.Name.OtherClass) but just
using A.Very.Long.Name.Space.Name;
Class...
OtherClass...
2. The use that @mikis3300 described
c sharp is despicable and requires twice the amount of code to the same thing as js
@@mimosvetajavascript requires less code to do things wrong you meant.
Basically, you create a variable that runs a custom code when it is not in scope anymore, this is really interesting, there are a lot of use-cases for this. The Async Dispose is confusing, I agree
You just explain wonderfully. Hope you do a video about Typescript experimental decorators
nice explanation and wonderful vid as always. IMO, this seems like a more complicated version of the with statement and context managers in python
seems like a version of the using in c# lol
It's a common practice; even C++ has some best practices for it. (RAII)
@@mehdi-vl5nn This video reminded me exactly of smart pointers in c++
In C# at least, the with statement needs its own scope. Later versions of C# introduced using as well, wich makes the code much more readable, especially in cases when multiple disposable varibles needs to be created.
@@mehdi-vl5nn raii is far less complicated and much more readable than this. and honestly, there are much better methods nowadays, like what python or rust does.
Such a great point about await - that was my gut instinct react: "does the other code need to wait for it now?" I wonder how/if they're going to change this...
Imagine postgres connections..
await using connection = await pg.connect();
Makes more sense to say "async using" or just letting using look for asyncDispose and call it automatically.
what a powerfull option to keep stuff clean, handling everything behind scene
I agree with you. I think “using async symbol = expression” would be a better syntax as the symbol is async, it’s not awaiting. And the modifiers (using or async) could be in any order, however that would reduce compiler optimization to be supported. I still think it is a better syntax to accomplish same results.
I believe the choice for await is due to parent scope checking, as it must be inside a async scope, and therefore using the word await has the compiler benefit of checking the parent scope to be async, where any other word could make that task a bit harder or even harder to debug as async would check for parent scope only when used along with “using”…
Add my vote to the chorus who observe the obvious inspiration from the Python "with ... as ..." construct.
One strength of the Python approach is that the resulting "withContext" is itself a full-blown object with two magic methods -- "__enter__" and "__exit__". This gives the developer the tools needed to reliably handle most situations, while greatly simplifying call sites.
One of the strongest motivators for adding the "with ... as ..." construct to Python was the literal impossibility of correctly handling surprisingly common failure scenarios with "try/except/finally" (the Python way of spelling "try/catch/finally"). The bottom line is that "with ... as ..." nearly always works, and is nearly always a better choice than "try/catch/finally".
I'm not a language designer or compiler developer, so I can't speak to the constraints that govern the addition of a new keyword to JS. I hope that the JS developers will more fully embrace the learnings from Python.
Perhaps something analogous to the "hooks" in React might improve the bare "using" keyword.
Love when learning new things from the channel
Easy explanation and use cases. Great video 👍
Great explanation ! Thank you for this video! I will watch more of your content
Great video, thanks! I saw that TypeScript 5.2 has that feature and I was told it’s like IAsyncDisposable in C# :)
The `await` keyword isn't really a problem, it's the same in C# too, it makes sense.
this is so much work for something that could be solved easily if you could defer statements like in golang
Great! It was the first video I have seen talking about "using". Thanks for introducing the thema. Very informative. It looks a good practice when need to dispose objects at the end of its visibility scope. Any ideia when it is going to be supported by major browsers for web development?
very nice explanation... oh! in Golang defer solves that issue in a single line
You are not alone in having reservations about repurposing *await* in "await using". However, *await* already does different things depending on the context in which it is used, consider "for await…of" statements. I think we should consider *await* to mean: opt into asynchronous behaviour.
History has come full circle. It's like getting 15 years back into .NET framework 2.0 and C# 🙂
Btw, why not use try / finally and close the file in finally block?
I have to admit that few years ago I started to dislike the path that JS evolution is going.
What next? Destructors?
I dont agree with your last point. If your cleanup is asynchronous, then the setup method is asynchronous as well. Some of the awaiting does happen on the line that has await, if its used correctly
The rapid growth of programming languages in general, and JavaScript in particular, has made me feel overwhelmed. I can't keep up anymore. Clients and employers keep asking us developers to work on one thing today and another the next day. The biggest regret in my career was switching from embedded to frontend development.
I feel the same way too 😩😩. So much things to consider for learning. Just when I thought the pain had been over, a new one arrives 😥😥
You are describing a non-issue in my opinion. This sort of feature is years away from making its way into any large codebase. This is only an issue if you work at a small startup company.
I personally like this because it's the beginning of a magic methods implementation in JS and I really enjoyed the creative freedom those brought in python and php
After 3 minutes of this video I remember why I love Ruby so much.
I agree with you. The await key should be a configuration inside the async disposal instead. When someone else uses my disposable they should not have to know if it's an async or sync disposal. JS should handle that for that developer!
This is similar to "with" keyword in python....good to see this in javascript
Interesting! Thank you for the video, Kyle!
seems to be really interesting changes. thanks for keeping us up2date. :)
Programming language 101: declaring variables. This should be very straightforward and Javascript makes it very weird.
I use a recursive function that reads folders and outputs an object with the folder structure. Wouldn't the dispose just slow down the execution when used inside the recursive function?
In go there is "defer" keyword which will simply run any code you want after the function finishes.
11:30 I think defer would be a better keyword for this purpose, but overall using seems very useful
Are you a gopher ? Golang Dev. Cases like this handled by defer keyword in go
@@barssavas9938 I don't really know that about Go, but I think defer makes sense, since it is deferring the dispose
Nice video. The syntax with await using is taken one to one from C#, I don't think it's confusing once you understand that await is on using (declaration) not on the assignment.
The syntax may be the same as is used in C#, but is the behavior the same? My understanding is that using the await keyword in C# actually blocks the execution thread while that is not the case in JavaScript. So would the example Kyle used where a log statement is called before the cleanup have the same behavior in C#?
@@shane3744 in c# it works in exactly the same way
If we're closing multiple asyncs -- does it do the serially, or does it do a promise.all?
as to await using -- yeah but ... I sympathize with the challenge of how else to do it better.
is it really javascript or typescript only ?
Nice video! I think I need to do more research in the asyncDispose. Would node fail if I use async close without asyncDispose? Or would it manage the close of files before exiting the program?
They could have implemented this feature as a class Destructor function. That would have been more obvious and convenient.
That is not how devs use classes in js
That is not possible in a garbage collection-based language. You cannot guarantee the lifetime of objects.
I’ve never used classes in JavaScript, I’d much prefer something that doesn’t force me to use something I’ll never use 😂
Very good insight 💯
Great video! The Symbol syntax is a bit weird, also what even is Symbol? I know zod supports symbols now, but what are they for?
if i want to add return type to the function what should i use?
{handle: Type, [Symbol.dispose]: () => any} ??
if so, it's probably better to make a custom Type?
type DisposeType = T extends Promise ? {handle: T, [Symbol.asyncDispose]: () => Promise} : {handle: T, [Symbol.dispose]: () => any}
👀
Ok, JS invented disposables. Well played! 🎉
Awesome ! 🔥
It’s fine and similar to Python’s “with” keyword. But for JS…sounds good idea considering that so many new devs mess up the memory mgmt.
Just like (with-* ...) macros in Common Lisp, they will execute some code after end, even though it has thrown exception:
(with-open-file (s "file.txt" :direction :output :if-exists :supersede)
(format s "Here are a couple~%of test data lines~%"))
Nice! C# is being doing that since 10 years.
6:12 Why did it call dispose twice?
How you can get the update information like this ? which is official document about js ?
This will be interesting for frontend frameworks.
If your close is async, wouldn't your open also generally be async?
In the C# version of async/await (the original, I believe), await using opens a new block and the assignment is usually a constructor which isn't async. It makes a little more sense given that you can consider it to be awaiting the completion of the entire block.
await using(var f=new File()) { /* do whatever with f */ }
Using declarations (without the block open) were added later and behave just like these in js but it's just a shorthand for a block that goes to the end of the scope that the using is in to avoid an indent.
I love how JS tries to complicate things, Go has a beautiful and simple implementation with defer for same scenarios.
How is this feature complicating things? This allows much more freedom in a compact way. Other languages like C# (using) and Python (with) already have features like this.
defer in go is amazing compared to this mess
Whilst I don't have any immediate uses for 'using' I do like what it does. But I also agree that that extra 'await' is annoying. I feels like it's coming up against the quirky design of async/await which I've fought with before in other sructures.
how to get this experimental typescript version?
"npm install -g typescript@latest" command just gives me version 5.1.6
Is that the "context manager" like in Python?
If so, Python has solved it using keyword "with", e. g.
with open("my_file.txt", "r") as my_file:
...
As for asynchronous context manager
async with ... : # some body
What is imho prettier, it creates it's own scope (by indentation), unlike this in JS, where it sits in the scope where the variable is created.
Hi Kyle, do you still recommend taking your Free React Hooks course if I have your new React Course. I noticed you go over some of the same hooks, so I'm wondering if it would be redundant to do both now. Thank-you
I love the explanation and looking forward to mainstream adoption, but what are your thoughts on try/catch - finally? I use finally to close DB connections and files after use usually and catch to obviously catch the errors for logs etc. but because finally always runs regardless of the outcome of the try, its a good place to clean up.. no?
Im using node 20.6 and trying to run this code
function openFile() {
return {
handle: 1,
[Symbol.dispose]() {
console.log('dispose handle');
}
}
}
function foo() {
using f = openFile();
}
foo();
but I get error message SyntaxError: Unexpected identifier 'f'
what is wrong wit it? or what is wrong with me?
The "defer" keyword from Go might also be useful.
shouldnt it be by default that a opened file closes if it gets out of scope? I have the feeling creating another keyword just for something like that seems to complicate things more at the end.
This is a problem with async/await I ran into once in a while, and has not very much to do with the keyword using. There are a lot of examples where await doesn't wait. But you have to check to notice this like you just did.
Using sounds great. Could be useful. Especially for libraries.
Your example is a bit weird. Guards should always be written at top of the function of course. Return before you do anything. And in javascript most if statement should be false/falsy. If path doesn't include temp write permanent. No return.
I won't lie, I did not understand a thing, I'm still learning 😂😂😂 but I'd really want to know is what color theme are you using? It looks great😊
I was going to say JS is becoming such a meme, but "using" seems useful.
Why is the syntax Isn’t like `using a = await openFile()` similar to `var a = await openFile()`?
I loved this feature
I progeamm api in python and clients with angular and typescript. For python it is realy simple to handle stuff like this, with the keayword "with" you can declare a namespace where the file is open and when you leave the namespace got closed, i miss this in javascript and typescript
Thank you that is awesomeee.
Can you share your tsconfig and package.json file please, I just can't get nodemon to work with TypeScript and ESM modules
What's the new guitar in the background? Is that a seven string?
JS mimics Rust ❤
Well, technically this could be done using finally. But this approach allows cleaner code and less indentations, especially when several such variables are introduced in one function in different places.
It seems like this functionality should be built into the vanilla fs functions. I don't think most coders are going to want to make their own wrappers.
This is cool but I prefer the current way of doing this using a weak reference. Just define a global function like "onCleanup", that you pass an object and a function, then the function you pass gets called when there are no more references to the object and its been garbage collected. Only downside is its restricted to being asynchronous and the object is no longer available after its been cleaned up. You can get around loosing the reference by using a proxy as a wrapper and watching the proxy for garbage collection though, and then your reference in the cleanup code wont prevent it from being GC'ed
Async would make more sense rather than await. Alternatively nothing.. still, nice to have something similar to python’s `with` keyword ( context managers)
What is that square bracket thing where Symbol is written? JS keep on changing at the speed of light. smh.
Why didn't you use top-level await? You don't have to wrap it in an async function
Top-level await only works in ESM modules and isn't supported in Node's default CommonJS modules. I'm guessing Kyle just didn't configure his example to use ESM modules, but it would have been valid to use top-level await if he had.
When I am old Javascript will have 5000 keywords.
With the way it is implemented, why do we have to make the function call Aynsc and put the await keyword in front of using?
That means if the underlying function changes all the callers have to change, yes they might have to do that anyways but this forces it
2:47 i knew right away right at 0:02 that its copied from python & context managers
The await keyword in your demo, is actually making perfect sense. It awaits the "open" method, and the disposal is still handled automatically (which has nothing to do with the open method). So I think everything is fine, and as you would expect coming from another language.
No, look at the openFile function in 4:49. It's not async, what is async is the closing of the file in the Symbol function it returns. It opens the file synchronously but closes it asynchronously.
If the openFile method took 2 seconds to complete, then it would have been more clear. If the openFile is async, execution would not block all scripts, but continue (somewhere else), until the file handle is returned. If the openFile method is synchroneous, all execution would then have been blocked for 2 seconds.
It's not illegal to call sync methods within an async execution frame, but you won't benefit. So the placement of the async keyword makes sense. But imagine that he instead of openFile had made an example with a method like OpenHugeFileAndReadAllAsync (which actually took several seconds), it would have been a better example of why you need the async method, and why it makes sense to write it in this way. This would demonstrate that all other async code in the application would have continued to execute, while you at the same time are executing a heavy IO operation.
This sounds like a great feature.
Would've preferred to have the defer keyword from golang instead :D
So in your example, if you call the "openFile" function without defining it's output with "using", then your file will never get closed.
You'd have to be sure that either you function documentation is really good, and people actually read it :(
Or your developers would always have to look into that function to be sure of what it's doing.
I don't see a huge benefit over:
try {
const file = ...;
// Do something with the file.
}
catch (err) {
// Do something useful.
}
finally {
file.closeSync();
}
You don't see the benefit of changing a single word over 7 additional lines which include control flow and additional braces? You already have to do that sort of documentation if you return something that needs to be closed, it just makes is much easier to work into a large variety of flows. Obviously if you have to write the openFile function yourself it isn't great, but presumably libraries like "fs" will include dispose options by default in the future and changing "const" to "using" handles the rest.
No, you guys have to stop using fs and start using fs/promises instead. No more opening and closing files manually.
@@maelstrom57 I can certainly agree with that, but this general pattern of activity comes up sometimes, and this is a useful tool for handling it.
@@maelstrom57 true, but I was just using the try-catch-finally method as an obvious replacement.
@@owenneilb I find that the way that "using" was shown makes the code less obvious.
If you have developers who know what they're doing it can be ok, but if you have a lot of junior developers, or just inexperienced developers, then more clear code is less likely to cause issues.
I was a bit confused why you would show a new JavaScript feature within TypeScript.
C# coming to JS! Great!
In C I go for a tiny function that sandwiches writeInOpenFile(file)
This may seem inconvenient, but it leaves no questions to the reader.
I don't do it in Python because the with keyword gives the same guarantee.
The scenario you are providing to explain the new keyword requires you to use an experimental feature with a Pollyfill that makes the example harder not only to test but to understand as well.
How else to solve the issue of async disposal?
Is that a ts only feature, or is it a JS thing?
This is a feature coming to JS (which is why TS is implementing it). You can see the full proposal here. github.com/tc39/proposal-explicit-resource-management
so it's like try with resources in java?
"async using" would make much more sense than "await using"
Is this similar to the “try with resources” concept in Java?
When you show new features like this one, it would be great if you added some links in the description referencing the specification or feature discussion.
I am wondering why it was decided to make it that complicated and not add destructors like we have them in other languages as a complement to the constructors we already have in classes.
Reminds me on kotlins 'use' lambda function that handles autoclosables automatically
This seems like an overly complicated / error prone version of Swift’s defer. It’s more powerful for sure, but that power doesn’t seem necessary.
I suppose someone could use this to make a defer function though. You could make the defer function use using internally and call some block when the defer function is out of scope… I think.