23 ms·
V8 adds support for top-level await
- miguelmota 7y agoThis is huge! Finally no more need to use IIFE's for top level awaits
- danappelxx 7y agoWhat is an IIFE?
- carreau 7y agoImmediately Invoked Function Expression: (function () { statements })();
- CryoLogic 7y agoImmediately invoked function expression - aka an anonymous function that executes as it is interpreted. (function() { console.log('test'); })(); In old versions of JavaScript, variables had to be scoped to the nearest function (rather than the nearest block) so IIFE where often used for namespacing: var $ = (function() { this.version = '1.2.3'; })(); $.version; // 1.2.3 This worked because the function would be immediately invoked upon interpretation, scoping it's contents to itself (aliased by the variable name).
- furgooswft13 7y agoHate to be a wet blanket, but that second code sample won't work. The function doesn't return anything so $ === undefined. Even if it did, this in that context is bound to the global object, so is the same as setting a variable without var or let. A proper example of IIFE for lexical scoping would be something like this: var numbers = ""; (function(){ for (var i=0; i<5; i++) { numbers += i + " "; } })(); console.log(i) //undefined Nowadays of course you could just use let instead of var Your code snippet would kind of work as expected if used as a constructor. As in: var $ = function() { this.version = '1.2.3'; }; var obj = new $; console.log(obj.version); //1.2.3 Of course that is not a IIFE. Ah...brings me back to my SO days...
- momokoko 7y agoHate to dry off the blanket but they just forgot the `new` keyword for `this` ;) var $ = new (function() { this.version = '1.2.3'; })(); console.log($.version); // '1.2.3' EDIT: Or if you really want to abuse the spec var $ = (function() { if (!(this instanceof arguments.callee)) return new arguments.callee(); this.version = '1.2.3' })(); console.log($.version); // '1.2.3'
- furgooswft13 7y agolol. But yes, I do really want to abuse the spec. You could also do this: var $ = (function(){ this.version = '1.2.3'; return this; }).call({}) console.log($.version); //1.2.3 But that's not quite as ugly so I guess I lose some points.
- 11235813213455 7y agohe should have said IIAFE actually (immediately-invoked-async-function-expression) (async () => { console.log(await 'hello world') })()
- anonytrary 7y agoAll async functions are functions, so not really.
- adrianpike 7y agoImmediately Invoked Function Expression; (function() { alert('swiggity swooty'); })();
- 52-6F-62 7y agoThis one got my vote just for swiggity swooty. Swiggity swooty, indeed.
- alexanderdmitri 7y agoAlso, I believe TypeScript uses these to transpile classes with public and private properties/methods (can't verify that's still case atm). It's a really neat pattern imo, I remember it fondly from my JS heavy days :)
- munchbunny 7y agoIt's a useful pattern that I used a lot, though I don't know if I would agree that it's neat. I definitely prefer the explicit annotations or even the python underscore convention.
- Zarel 7y agoNo, while this is sometimes used for private properties/methods, TypeScript never used it. TypeScript's private properties/methods are just compile-time errors, they're still public in the generated code.
- alexanderdmitri 7y agoYou're right[0], which is weird because they got most of the way there. I guess I just assumed since they were leveraging IIFEs this would be a natural use case. I'd be interested in knowing why they didn't actually. According to this[1] exchange on Stack Overflow, the IIFE is used by TypeScript because of other scoping issues (specifically protecting class properties before instantiation and defining interfaces). [0] https://yakovfain.com/2015/06/30/the-private-in-typescript-is-kinda-private/ https://yakovfain.com/2015/06/30/the-private-in-typescript-i... [1] https://stackoverflow.com/questions/56086411/why-does-typescript-pack-a-class-in-an-iife https://stackoverflow.com/questions/56086411/why-does-typesc...
- tlrobinson 7y agoAn easily Googleable acronym.
- megous 7y agoIt's nice, I guess, but huge? Instead of: async function main() { // code } main().catch(console.error); I'll be maybe writing: try { // code } catch (ex) { console.error(ex); } Hrm?
- casret 7y agoOne thing that has bitten me is when you are using vm.run* and friends, it's impossible to get a result of it if you need to use async calls. Hopefully this solves that case. There is also the original use case that the original proposal of needing to do async calls when during module loading.
- mattwad 7y agoI think the boon here will be for scripts that run once. Say I just want to query a few tables. Now, I no longer need a `main()` wrapper: const result = await db.events.find(); console.log(result); That's my whole script
- james-mcelwain 7y agoOr my preferred (gross) version: void async function main() { // code }()
- elyobo 7y agoMine all tend to look like (async () => { // code })().catch(console.error)
- jraph 7y agoWow, I had completely forgotten the existence of void in JavaScript. Why is it needed here? To force an expression without using the parentheses? Edit: Yes, it seems so, and this usage with functions is explicitly documented there: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- 7y ago
- paulddraper 7y agoThough it goes even beyond that. It not only works for the main entrypoint module, but all other modules as well. Though I can say I've ever encountered a reason to have that....
- deleted 7y ago[deleted]
- nilkanthjp 7y agoSeems convenient. Though I've never thought of top-level async as the killer feature I've been waiting for out of V8. Unless I'm missing something about what this enables...
- tedivm 7y agoNow if only python would do the same.
- iikoolpp 7y agoImpossible due to how async is implemented (as generators).
- pitaj 7y agoJS async is implemented as generators as well AFAIK
- dlbucci 7y agoNot unless promises are implemented with generators. I dunno though, I feel like there's some overlap between the two concepts, so maybe they are?
- pitaj 7y agoPromises are just objects which represent an asynchronous value. Async/await are coroutines: they have a shared context, stack, etc. They can be paused and resumed. The implementation of async/await depends on the engine, but I'm pretty sure they use the same generator backend. Babel and other transpilers convert async functions actual generator functions. By contrast, promises are a fairly simple runtime library.
- Scooty 7y agoI'm not sure how they're handled internally, but async functions are no longer generators. For a while after generators and before async/await, generators were used as a polyfill.
- pitaj 7y agoBecause of how similar they are, it would make sense to implement async/await using existing generator backends in the VM, right?
- lacampbell 7y agoIs this part of the standard?
- paulddraper 7y agoDid you read the commit? The spec material was linked: https://tc39.es/proposal-top-level-await/#sec-execute-async-module https://tc39.es/proposal-top-level-await/#sec-execute-async-... It's a Stage 3 proposal https://github.com/tc39/proposal-top-level-await https://github.com/tc39/proposal-top-level-await
- lacampbell 7y agoI did. It looked like a diff, and since I don't have much interest in the internals of v8 I closed the tab. Welcome to the internet!
- mstade 7y agoIt's a stage 3 proposal[1], which according to the TC39 process[2] means "the solution is complete and no further work is possible without implementation experience, significant usage and external feedback." In other words, it's all but standardized. Barring significant blockers coming from actual implementation experience this will most likely be ratified. [1]: https://github.com/tc39/proposal-top-level-await https://github.com/tc39/proposal-top-level-await [2]: https://tc39.es/process-document/ https://tc39.es/process-document/
- Klathmon 7y agoAnd the way the TC39 works, it won't progress until multiple vendors can implement it. Being implemented by multiple js engines is how JS features become standardized.
- mstade 7y agoYup, thanks for adding that. It's a good process I think, though it is kind of a double edged sword. On the one hand it's nice because it means the standard isn't bloated with a bunch of stuff that no one implements, but on the other hand it also means it's much harder to get rid of stuff that turns out to maybe not be such a great idea after all, leading to bloat anyway. Still, it's a pretty good process I think.
- 1wheel 7y agoAwesome! How long before this shows up in canary?
- The_rationalist 7y agoDoes rust async support this?
- pitaj 7y agoRust async is just syntax: there is no event loop built into the language.
- Rusky 7y agoSome async runtime libraries have started offering an attribute on `fn main` to make it async, which is pretty close to the same thing.
- monocasa 7y agoTokio does with a #[tokio:main] attribute on the main function.
- londons_explore 7y agoAll this async code without decent locking primitives is leading to a rabbit hole of race conditions... It doesn't matter that it's all single threaded if all your function calls may or may not block and run a bunch of other code in the meantime, mutating all kinds of state. I feel like JavaScript developers of the 2020's are going to relearn the same things the C programmers of the 1990's learn, just a few levels of abstraction higher.
- duxup 7y agoIsn't every new language doomed to relearn everything in some way?
- verisimilitudes 7y agoNot everyone uses languages designed by people with no place designing a language and taken from there. Not every language is PHP, JavaScript, C++, et al. Take a look at a Lisp, APL or a derivative, or Ada for examples of languages designed by people who knew what they were doing. Lisp grew in universities under the direction of hackers and there are several standard dialects now. APL was designed by a mathematician originally as a teaching aid. Ada was designed in a contest by the US DoD after spending a long while collecting requirements and it was then fully specified before any implementation work was done.
- munificent 7y ago> by people who knew what they were doing. The Great Old Ones did not have access to any lost mystical knowledge. The Lisp inventors hadn't even sorted out variable scoping leading many Lisps to have dynamic scoping which is now widely agreed to be the wrong default. Also, the Lisp-1 versus Lisp-2 split. These were clearly people that were figuring it out as they went along, which they would be the first to admit. > standard dialects An oxymoron if there ever was one. :) > APL was designed by a mathematician originally as a teaching aid. ...and it's virtually dead. There are many interesting ideas in there but it doesn't seem like Iverson's ideas about what is a desirable notation for thought resonated with any significant fraction of people. There were many years where APL was a standard part of a CS degree. With that much forced contact, you would expect it to have stuck around if it had so much going for it. (See: Pascal and BASIC, which punched above their weight in large part because of early introduction in schools.) > Ada was designed in a contest by the US DoD after spending a long while collecting requirements and it was then fully specified before any implementation work was done. Ada also has a lot of interesting ideas but... outside of the DoD, it's not exactly doing well either. It turns out that spending several years writing an enormous specification without regard to implementation leads to... unbelievably complex, expensive implementations that take years to reach the market. Who could have predicted that? The economic cost of implementing the language is a salient part of its fitness. It doesn't matter how nice the language is if you can't actually use it because you don't have a working compiler. C++ is only bad to the degree that you can completely wish away path dependence. (Hint: You can't.) There were many languages better than C++ designed at the time, but they didn't take off because they lacked the features we hate in C++ today. Those features existed as a path to lead the existing C programmers to C++. Meanwhile, other purer languages floated off in space, beautiful but unreachable by mortals. JavaScript was just a sad rush job that we're all unfortunately stuck with. I agree with you on PHP.
- deleted 7y ago[deleted]
- mstade 7y agoI think async/await probably makes more sense in a typed language, where a compiler can tell you when you're missing an await, or at least warn you about not dealing with potential side effects and error handling. For something like JavaScript, it'd make more sense to me to have the runtime always and implicitly await the result of async functions, and instead make developers explicitly say when they wish for the result to be async. For example, instead of: const data = await fetch() push(someData) // async, runs in the background You would do: const data = fetch() // Runtime detects promise and awaits result async push(data) // the async keyword would return a promise and execute the push function asynchronously, allowing the next line to execute In this fantasy world the "await" keyword would work anywhere and as you'd expect – awaiting the result of any promise: const data = fetch() // implicitly await await async push(data) // this would also be "synchronous" in that it suspends execution of subsequent code until `push` fulfills or rejects the promise, and so it'd have the same effect as implicit await Point is you'd probably await that promise elsewhere, so you'd actually store away the return value of the `async` call and later on you'd `await`, or another example would be to await a block. Promise rejections in implicit awaits would halt execution, just like a sync function throwing an error, so you wouldn't "miss" an error somewhere because runtimes swallow promise rejections. (Well, at least Node wised up eventually.) This means there'd be no difference in function declaration between sync and async functions, it'd be determined by whether they return a promise or not which I think should be possible to statically determine by a JIT compiler in most cases, so not adding too much (if any) overhead. Kind of a half baked thought, but point is I always felt the async/await thing was kind of backwards in JavaScript.
- minitech 7y agoPromises are nice in JavaScript because they’re just a value with no magic. Anything can create them, not just async functions. Generic/higher-order functions and so on can get involved without needing to know the difference. A proposal to introduce magic at calls sounds really awful, sorry.
- mstade 7y agoNo magic is rich — they swallow errors, for one. In any case, I don't think anything I said precludes the creation of promises outside of async functions, in fact quite the opposite. The difference is that the runtime would implicitly await the resolution of a promise returned from a function (any function, there'd be no such thing as an "async function") and if you actually wanted things to progress asyncronously you'd simply add `async` before the statement and it'd return a promise (for keeps) of the value of the statement, which could be the return value of a function, or even something as simple as: const val = async (1 + 2) await val // 3 Maybe it's a genuinely dumb idea, for reasons I can't fathom, but it seems to me this would make working with async code much simpler than it is now, where if you forget an await you've probably introduced a bug, and the only way to know if you need await is to peruse docs (hoping that they're accurate) or judiciously sprinkle it everywhere.
- thatguyagain 7y ago[removed]
- jhanschoo 7y agoNothing. It's just that you can have const result = await someFetchOrSomething(str); return result.foo; while not inside an async function. You still can't use it in a non-async function.
- efdee 7y agoYou can't return when you're not in a function ;-)
- efdee 7y agoNothing would change here. Normally, await can only appear in a method that has been marked as async. However, when you write toplevel code (code that's not wrapped in a function), you can't mark the function as async (because there is no function) so you can't use await. Enabling toplevel await fixes that.
- bandrami 7y agoSo COMEFROM is now a first-class feature of the most popular programming language in the world. Intercal really was ahead of its time.
- nickitolas 7y agoCould you elaborate?
- braythwayt 7y agoCOMEFROM began as a joke comtrol-flow construct, but the principle turns out to have a deep semantic value: https://en.wikipedia.org/wiki/COMEFROM https://en.wikipedia.org/wiki/COMEFROM Aspect-Oriented Programming is a kind of COMEFROM. The proposition in the comment is that await is another.
- pjmlp 7y agoJust like Lisp and Algol 68 on other domains, apparently good features tend to take time to become mainstream.
- bandrami 7y agoIt's a good point, and I remember even some fairly "serious" comp sci people saying COMEFROM was less of a joke than it originally seemed.
- bzbarsky 7y agoI'm not sure I quite see the analogy to COMEFROM. The "problem" with COMEFROM is that at the "target" location (the one control comes from) there is no in-source indication of the control flow transfer. So what looks like linear code turns out to have this unexpected detour to the location of the COMEFROM instruction. This hinders understandability of the code. "await" in JS doesn't have that problem: there is an explicit control flow operation, in the form of resolving a promise, that eventually transfers control to the location of the "await" call. And even then, it's async from a run-to-completion perspective, so doesn't affect linear code, modulo other await calls. I guess the concern is that you could have linear code that calls a function, which does an "await" and you would effectively have a control flow detour that's hidden from view? In that sense, I guess this is sort of like COMEFROM... At least you have to explicitly opt in (via "async function", or async module) for it to be a problem. Anyway, a better analogy from my point of view is that async/await is a very limited form of call-with-current-continuation or so. And with generator functions, JS already had that sort of, but without some of the nice ergonomics.
- self_awareness 7y agoThis looks like Dart's handling of async/await.
- Footkerchief 7y agoFinally JS finishes reinventing the benefits of imperative programming.
- galaxyLogic 7y agoHere's a counter-argument: https://gist.github.com/Rich-Harris/0b6f317657f5167663b493c722647221 https://gist.github.com/Rich-Harris/0b6f317657f5167663b493c7...
- hayd 7y agoWas any of these concerns allayed?
- w56rjrtyu6ru 7y agoJust stick to a sane functional style and you should be OK. I would mostly consider stateful modules an antipattern in any long-running piece of software such as a Web app (server or client). Normally you'd like to use some sort of explicit state store (Redux, database, etc) in an app like that. Stateful modules (whether sync or async) make testing harder - suddenly you're exposed to the "wonderful" world of dep injection, monkey patching and mock libraries. Isolating different features of your program for testing should be as easy as passing a different set of arguments to the entry point of that code path. On the other hand, when working on some sort of "run-once" script, it's perfectly fine to use modules as a unit of encapsulation of state. That's where async imports can really shine - suddenly you don't need to alienate readers of your program with callback hell, .then() chains or IIFEs, which are in effect just a bunch of redundant punctuation that gives JavaScript a bad name.
- craftoman 7y agoI was waiting years for this. From now on code will be much more readable and cleaner without all those IIFEs.
- w56rjrtyu6ru 7y agoOne reason why this is actually big is it reduces the boilerplate needed to write shell scripts in Node. I think seemingly tiny moves like this can reduce a certain cognitive overhead/feeling of unfamiliarity associated with learning a programming language. Essentially, I am hoping that this will help bring new talent to the desktop OS space, effectively allowing Web devs to "break free" from browsers/Electron/REST server. Maybe it's just me, but I find that ES somehow manages to be useful over a wide spectrum of problem complexities, i.e. it seems that a JavaScript program may grow more gracefully than one written in Bash. (Though I've gained new appreciation of Bash since I first stated this on HN and got downvoted to hell for failing to speak in common idioms.)