Instead of:
async function main() {
// code
}
main().catch(console.error);
I'll be maybe writing: try {
// code
} catch (ex) {
console.error(ex);
}
Hrm?Instead of:
async function main() {
// code
}
main().catch(console.error);
I'll be maybe writing: try {
// code
} catch (ex) {
console.error(ex);
}
Hrm?To me this is most important in node where it's not uncommon to do async operations during initialization. Currently you either have to export a promise or an async function.
Is the argument for top-level await that you don't need the other spot? Because I feel like you still do - except now it's implicitly inside not only A, but likely B and C as well to some extent. And in a very inflexible way.
Synchronous effectful imports are already are the source of so much frustration, and now we're adding asynchronicity to it?
Just export a init function and let your users choose when/where to run your effectful initialization code, sync or async.
fetch(allResourcesUrl).then(async response => {
let allResources = await response.json();
let pageResource = await (await fetch(allResources.page1.url)).json();
// set up page with pageResource
});
can be replaced with {
let allResources = await (await fetch(allResourcesUrl)).json());
let pageResource = await (await fetch(allResources.page1.url)).json();
//set up page with pageResource
}
Note use of a top-level block instead of a function context to hide local variables.I think the onus would be on you to prove that using a less restrictive concept is worthwhile because it saves you two keystrokes. That, in contrast, seems like the opposite of good engineering. Like using classes over structs because class is shorter to type.
The more I think about your post, the more absurd it becomes.
On the other hand, there are some good reasons not to use use const everywhere we can. Paul Sweeney lists a few here: https://medium.com/@PepsRyuu/use-let-by-default-not-const-58...
I'm not sure where I land yet. Perhaps it's a decision to make separately for each codebase.
const x = 1;
(function() {
const x = 2;
console.log(x);
})()
or from an argument: const x = 1;
(function(x) {
console.log(x);
})(2)
So it has limited value in any code that uses closures.If you've declared const x = 1, then that will hold for your scope?
If you go into a separate scope... Well, then you're in a separate scope?
In an ideal world, const would be the default, and you would have to opt into non-const.
> There has never been a bug caused by reassigning a function local variable while leaving it mutable.
The latter _is_ mutable state. const doesn't prevent all mutations, but it does prevent some, while let prevents none. I'll take the limited protections of const (with awareness of its limitations; many examples of let are of confused devs trying to avoid making their objects immutable).
The popular airbnb eslint rules[1] require the use of const where a variable is not reassigned, which I've occasionally found handy.
[1] https://github.com/airbnb/javascript#references--prefer-cons...
// code
Outputting errors to console is the default behavior.The only difference it will make here is to suppress the default stack trace[1] and errorlevel returned to the shell. If you are writing in this kind of "scripting" style, you probably don't want to suppress errorlevels, so leaving out the catch is not only simpler, it is safer.
[1] You may get a stack trace with an Error object, but not the default one from the Node process.
That's not necessarily the case (or maybe poorly worded), e.g.:
async function main() {
// code
setInterval(() => console.log('hey'), 1000)
}
main()
In real life, it would probably be a HTTP server holding up the process. It's true that you likely want to crash hard if you have unhandled error during server initialization but I would still catch the error because Node.js will otherwise print a bunch of ugly "UnhandledPromiseRejectionWarning" messages. E.g.: main().catch(err => {
console.error(err);
process.exit(1);
});
With top level async landing in v8, I guess Node.js will eventually stop printing those warning messages.I did a quick test just to see how bad things might get if you were to rely on orphaned timers like this. The important thing to keep in mind is that each of those timers is effectively its own thread, which can throw errors and terminate itself. But if those are orphaned outside of a runloop that can handle those errors, they will not be caught by the catch outside of main (which only catches errors thrown in the "main thread"). Rather, they become top-level, uncaught exceptions, which also terminate the process.
Here's an example illustrating what happens:
async function main() {
setTimeout( () => console.log( 'Hello' ), 3000 );
setTimeout( function() { throw new Error('error 1'); }, 1000 );
setTimeout( function() { throw new Error('error 2'); }, 2000 );
throw new Error('oops');
}
main().catch( x => console.error( x ) );
The result here is that `oops` is displayed (the caught error from the "main thread") and then `error 1` is displayed, but the process is then immediately terminated. Neither `error 2` nor `Hello` are displayed.Note that I use the term "thread" loosely, in the "green thread" sense. Hopefully the meaning is clear.
There is also the original use case that the original proposal of needing to do async calls when during module loading.
const result = await db.events.find();
console.log(result);
That's my whole script void async function main() {
// code
}() (async () => {
// code
})().catch(console.error)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/Refe...