sleep() await is only valid in async function [duplicate] - javascript

I have been going over async/await and after going over several articles, I decided to test things myself. However, I can't seem to wrap my head around why this does not work:
async function main() {
var value = await Promise.resolve('Hey there');
console.log('inside: ' + value);
return value;
}
var text = main();
console.log('outside: ' + text);
The console outputs the following (node v8.6.0) :
> outside: [object Promise]
> inside: Hey there
Why does the log message inside the function execute afterwards? I thought the reason async/await was created was in order to perform synchronous execution using asynchronous tasks.
Is there a way could I use the value returned inside the function without using a .then() after main()?

I can't seem to wrap my head around why this does not work.
Because main returns a promise; all async functions do.
At the top level, you must either:
Use top-level await (proposal, MDN; ES2022, broadly supported in modern environments) that allows top-level use of await in a module.
or
Use a top-level async function that never rejects (unless you want "unhandled rejection" errors).
or
Use then and catch.
#1 top-level await in a module
You can use await at the top-level of a module. Your module won't finish loading until the promise you await settles (meaning any module waiting for your module to load won't finish loading until the promise settles). If the promise is rejected, your module will fail to load. Typically, top-level await is used in situations where your module won't be able to do its work until the promise is settled and won't be able to do it at all unless the promise is fulfilled, so that's fine:
const text = await main();
console.log(text);
If your module can continue to work even if the promise is rejected, you could wrap the top-level await in a try/catch:
// In a module, once the top-level `await` proposal lands
try {
const text = await main();
console.log(text);
} catch (e) {
// Deal with the fact the chain failed
}
// `text` is not available here
when a module using top-level await is evaluated, it returns a promise to the module loader (like an async function does), which waits until that promise is settled before evaluating the bodies of any modules that depend on it.
You can't use await at the top level of a non-module script, only in modules.
#2 - Top-level async function that never rejects
(async () => {
try {
const text = await main();
console.log(text);
} catch (e) {
// Deal with the fact the chain failed
}
// `text` is not available here
})();
// `text` is not available here, either, and code here is reached before the promise settles
// and before the code after `await` in the main function above runs
Notice the catch; you must handle promise rejections / async exceptions, since nothing else is going to; you have no caller to pass them on to (unlike with #1 above, where your "caller" is the module loader). If you prefer, you could do that on the result of calling it via the catch function (rather than try/catch syntax):
(async () => {
const text = await main();
console.log(text);
})().catch(e => {
// Deal with the fact the chain failed
});
// `text` is not available here, and code here is reached before the promise settles
// and before the code after `await` in the main function above runs
...which is a bit more concise, though it somewhat mixes models (async/await and explicit promise callbacks), which I'd normally otherwise advise not to.
Or, of course, don't handle errors and just allow the "unhandled rejection" error.
#3 - then and catch
main()
.then(text => {
console.log(text);
})
.catch(err => {
// Deal with the fact the chain failed
});
// `text` is not available here, and code here is reached before the promise settles
// and the handlers above run
The catch handler will be called if errors occur in the chain or in your then handler. (Be sure your catch handler doesn't throw errors, as nothing is registered to handle them.)
Or both arguments to then:
main().then(
text => {
console.log(text);
},
err => {
// Deal with the fact the chain failed
}
);
// `text` is not available here, and code here is reached before the promise settles
// and the handlers above run
Again notice we're registering a rejection handler. But in this form, be sure that neither of your then callbacks throws any errors, since nothing is registered to handle them.

2021 answer: you can now use top level await in the current stable version of node
Most of the answers above are a little out of date or very verbose, so here's a quick example for node 14 onwards.
Make a file called runme.mjs:
import * as util from "util";
import { exec as lameExec } from "child_process";
const exec = util.promisify(lameExec);
const log = console.log.bind(console);
// Top level await works now
const { stdout, stderr } = await exec("ls -la");
log("Output:\n", stdout);
log("\n\nErrors:\n", stderr);
Run node runme.mjs
Output:
total 20
drwxr-xr-x 2 mike mike 4096 Aug 12 12:05 .
drwxr-xr-x 30 mike mike 4096 Aug 12 11:05 ..
-rw-r--r-- 1 mike mike 130 Aug 12 12:01 file.json
-rw-r--r-- 1 mike mike 770 Aug 12 12:12 runme.mjs
Errors:

Top-Level await has moved to stage 3 stage 4 (see namo's comment), so the answer to your question How can I use async/await at the top level? is to just use await:
const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)
Of if you want a main() function: add await to the call to main() :
async function main() {
var value = await Promise.resolve('Hey there');
console.log('inside: ' + value);
return value;
}
var text = await main();
console.log('outside: ' + text)
Compatibility
v8 since Oct 2019
the REPL in Chrome DevTools, Node.js and Safari web inspector
Node v13.3+ behind the flag --harmony-top-level-await
TypeScript 3.8+ (issue)
Deno since Oct 2019
Webpack#v5.0.0-alpha.15

To give some further info on top of current answers:
The contents of a node.js file are currently concatenated, in a string-like way, to form a function body.
For example if you have a file test.js:
// Amazing test file!
console.log('Test!');
Then node.js will secretly concatenate a function that looks like:
function(require, __dirname, ... perhaps more top-level properties) {
// Amazing test file!
console.log('Test!');
}
The major thing to note, is that the resulting function is NOT an async function. So you cannot use the term await directly inside of it!
But say you need to work with promises in this file, then there are two possible methods:
Don't use await directly inside the function
Don't use await at all
Option 1 requires us to create a new scope (and this scope can be async, because we have control over it):
// Amazing test file!
// Create a new async function (a new scope) and immediately call it!
(async () => {
await new Promise(...);
console.log('Test!');
})();
Option 2 requires us to use the object-oriented promise API (the less pretty but equally functional paradigm of working with promises)
// Amazing test file!
// Create some sort of promise...
let myPromise = new Promise(...);
// Now use the object-oriented API
myPromise.then(() => console.log('Test!'));
It would be interesting to see node add support for top-level await!

You can now use top level await in Node v13.3.0
import axios from "axios";
const { data } = await axios.get("https://api.namefake.com/");
console.log(data);
run it with --harmony-top-level-await flag
node --harmony-top-level-await index.js

The actual solution to this problem is to approach it differently.
Probably your goal is some sort of initialization which typically happens at the top level of an application.
The solution is to ensure that there is only ever one single JavaScript statement at the top level of your application. If you have only one statement at the top of your application, then you are free to use async/await at every other point everwhere (subject of course to normal syntax rules)
Put another way, wrap your entire top level in a function so that it is no longer the top level and that solves the question of how to run async/await at the top level of an application - you don't.
This is what the top level of your application should look like:
import {application} from './server'
application();

i like this clever syntax to do async work from an entrypoint
void async function main() {
await doSomeWork()
await doMoreWork()
}()

Other solutions were lacking some important details for POSIX compliance:
You need to ...
Report a 0 exit status on success and non-zero on fail.
Emit errors to stderr output stream.
#!/usr/bin/env node
async function main() {
// ... await stuff ...
}
// POSIX compliant apps should report an exit status
main()
.then(() => {
process.exit(0);
})
.catch(err => {
console.error(err); // Writes to stderr
process.exit(1);
});
If you're using a command line parser like commander, you may not need a main().
Example:
#!/usr/bin/env node
import commander from 'commander'
const program = new commander.Command();
program
.version("0.0.1")
.command("some-cmd")
.arguments("<my-arg1>")
.action(async (arg1: string) => {
// run some async action
});
program.parseAsync(process.argv)
.then(() => {
process.exit(0)
})
.catch(err => {
console.error(err.message || err);
if (err.stack) console.error(err.stack);
process.exit(1);
});

Node -
You can run node --experimental-repl-await while in the REPL. I'm not so sure about scripting.
Deno -
Deno already has it built in.

For Browser you need to add type="module"
without type="module"
<script>
const resp = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await resp.json();
console.log(users)
</script>
with type="module"
<!--script type="module" src="await.js" -->
<script type="module">
const resp = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await resp.json();
console.log(users)
</script>

You need to add type in package.json
"type": "module"
You are good to go.
import axios from 'axios';
const res = await axios.get('https://api.github.com/users/wesbos');
console.log(res.data);
Remember if you change type of document then you must have to write code in ES6 way.

Now with ECMAScript22, we can use await at the top-level module.
This is an example with ( await top-level ):
const response = await fetch("...");
console.log(response):
an other example without (await top-level )
async function callApi() {
const response = await fetch("...");
console.log(response)
}
callApi()

In NodeJS 14.8+, you can use top-level await module (#3 solution). You can rename also .js to .mjs (ES module) instead of .js (.cjs CommonJS).

If your only goal is to control the execution order of asynchronous code mixed with other code for testing purposes, you could wrap the entire top-level code inside of an immediately-invoked function expression (IIFE) defined as an async function. In the example from the question, you would then add await before calling main().
You can use this pattern when your code is not already in an async function or at the top level body of a module. In other words, if you're just testing a bunch of code inside of a js file and using tools like Live Server, RunJs, or any other type of JavaScript playground to watch the console window, wrap all of your code in an IIFE defined as async and use the await keyword when you want to wait for asynchronous code to finish before executing the next line.
let topLevelIIFE = (async () => {
async function main() {
var value = await Promise.resolve('Hey there');
console.log('inside: ' + value);
return value;
}
var text = await main();
console.log('outside: ' + text);
})()
You would not need to use this pattern when running the code specified in the body of the IIFE inside of the REPL in Chrome DevTools or another browser REPL tool that behaves similarly.

Since main() runs asynchronously it returns a promise. You have to get the result in then() method. And because then() returns promise too, you have to call process.exit() to end the program.
main()
.then(
(text) => { console.log('outside: ' + text) },
(err) => { console.log(err) }
)
.then(() => { process.exit() } )

Related

Because I can't run await on the top level, I have to put it into an async function - why can I then call that async function directly?

I have a short Node.js script where I require another package and call an async function from it and subsequently want to print the return value. If I simply await the return value from the top level, then I'll get an error, saying that I can only use await inside an async function itself. So apparently the way to go is like this:
async function main() {
foo = await someOtherAsyncFunc();
console.log(foo);
}
main()
Or:
(async function() {
foo = await someOtherAsyncFunc();
console.log(foo);
})();
Or:
(async () => {
foo = await someOtherAsyncFunc();
console.log(foo);
})();
(Credit to VLAZ in chat https://chat.stackoverflow.com/transcript/message/54186176#54186176)
This works - but I want to understand the reasons behind it a little bit more: I'm used to not being able to directly use await from the top level. However, I'm also used to having to call some special library function to actually "venture" into async from the top level. In Python, see asyncio.run for example. What's the point of requiring await to be inside an async function - if I can then call just any async function from the top level? Why then isn't await available at top level, too?
Top-level await used to not be a thing, but it is possible now in ES6 modules.
One reason why top-level await used to not be a thing, and is still not a thing outside of modules is that it could permit syntactical ambiguity. Async and await are valid variable names. outside of modules. If a non-module script permitted top-level await, then, short of re-working the specification (and breaking backwards compatibility), there would be circumstances when the parser couldn't determine whether a particular instance of await was a variable name, or was used as the syntax to wait for the Promise on its right-hand side to resolve.
To avoid any possibility of ambiguity, the parser, when parsing a section of code, essentially needs to have flags that indicate whether await is valid as an identifier at any given point, or whether it's valid as async syntax, and those two must never intersect.
Module scrips permit top-level await (now) because the use of await as an identifier has always been forbidden in them, so there is no syntactical ambiguity.
In contrast, there are zero issues with using .then on the top level because it doesn't result in any ambiguity in any circumstances.
Why doesn't it just return a Promise which is never executed because it doesn't get awaited?
Promises aren't really "executed". They can be constructed, or waited on to fulfill, or waited on to reject. If you have a Promise, you already have some ongoing code that will (probably) eventually result in a fulfillment value being assigned to the Promise.
Hanging Promises are syntactically permitted - values that resolve to Promises but which aren't interacted with elsewhere. (Which makes sense - every .then or .catch produces a new Promise. If every Promise had to be used by something else, you'd end up with an infinite regress.)
Doing
(async () => {
foo = await someOtherAsyncFunc();
console.log(foo);
})();
is essentially syntax sugar for
someOtherAsyncFunc()
.then((foo) => {
console.log(foo);
});
There's no need to tack anything else onto the end of either of those. (though it's recommended to add a .catch to a dangling Promise so unhandled rejections don't occur)

Await without Async in deno framework

Deno just released v1.0.
When I was checking getting started guild I show some unusual code.
import { serve } from "https://deno.land/std#0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
req.respond({ body: "Hello World\n" });
}
If you see for loop there is await without async.
So I'm wondering, is javascript async/await and deno await both are same or it's different?
Deno supports top-level await which is currently on stage 3.
Top-level await enables modules to act as big async functions: With
top-level await, ECMAScript Modules (ESM) can await resources, causing
other modules who import them to wait before they start evaluating
their body.
top-level await allows you to initialize a module with asynchronously fetched data.
// my-module.js
const res = await fetch('https://example.com/some-data');
export default await res.json();
// some module
import data from './my-module.js';
console.log(data); // { "some": "json" }
If you see for loop there is await without async.
Have in mind that functions using await will still need async keyword, even if top-level await is supported.
function foo() { // async is needed
const res = await fetch('https://example.com/api/json')
console.log(res);
}
foo();
The above snippet still throws an error.
References:
https://github.com/tc39/proposal-top-level-await
https://2ality.com/2019/12/top-level-await.html
https://v8.dev/features/top-level-await
Deno implements top-level await.
Top-level await enables developers to use the await keyword outside of async functions. It acts like a big async function causing other modules who import them to wait before they start evaluating their body.
Source: https://v8.dev/features/top-level-await

using await on global scope without async keyword

I am trying to do something like this on global scope in nodejs REPL. As per my understanding both the following statements are valid. see docs
let x = await Promise.resolve(2);
let y = await 2;
However, both these statements are throwing an error.
Can somebody explain why?
my node version is v8.9.4
Update
When using Node, the file currently must have an .mjs extension to work.
Top level awaits can be used in browser modules. When used the script tag must include the type attribute which must be set to module:
<script src="/script.js" type="module"></script>
const start = Date.now()
console.log('Pre call.')
await delayedCall()
console.log('Duration:', Date.now() - start)
function delayedCall() {
return new Promise(resolve => setTimeout(() => resolve(), 2000))
}
Working Node Example -- Run node ./index.mjs in the terminal.
Working Browser Example
Supported Versions
Old Answer
await can only be used within a function that is labeled async, so there are two ways you can approach this.
Note:
There is a proposal in place that may eventually allow the usage of Top level await calls.
The first way is to create a self invoked function like this:
(async function() {
let x = await Promise.resolve(2)
let y = await 2
console.log(x, y)
})()
Or the second way is to use .then()
Promise.resolve(2).then(async data => {
let x = data
let y = await 2
console.log(x, y)
})
This proposal is currently in stage 3 of the TC39 process. LINK
You can use this feature in Google Chrome and Mozilla Firefox as of now. You can use top level await without async in console.
https://twitter.com/addyosmani/status/1080365576218759168
As of version 13.3, Node.js support Top-level await.
Top-level await means you can now use await operator outside an async function. So both examples are correct:
(async function() {
await Promise.resolve(console.log('Hello await!'));
}());
// or
await Promise.resolve(console.log('Hello await!'));
Note: Top-level await only works at the top level of modules. There is no support for classic scripts or non-async functions.
Just keep in mind, that the await operator is used to wait for a Promise. It does NOT matter if you are using an await operator with a value other than a Promise. For example, the name variable in the displayName()` function:
async function displayName() {
const name = await 'unclexo';
console.log(name);
}
displayName(); // outputs 'unclexo'
As the value of the name variable is not a Promise, it converts the value to a resolved Promise, and waits for it. It happens under the hood.
The old behavior
MDN doc says
The await operator is used to wait for a Promise. It can only be used
inside an async function.
since node 10, you can run node process with --experimental-repl-await to allow top level await
https://nodejs.org/api/repl.html#repl_await_keyword
async function getTen() {
return 10;
}
(async () => {
let ten = await getTen();
console.log(ten);
})();
Source: https://javascript.plainenglish.io/5-javascript-interview-questions-to-identify-outstanding-developers-859a71c3d7f
Top-level-await is supported in Node.js version 13.3 or above
Example:
await Promise.resolve(console.log('๐ŸŽ‰')); // โ†’ ๐ŸŽ‰
(Though the question is about REPL, a note on running as script file. Set {"type": "module"} package.json or use the file name extension .mjs)
You could wrap all the code in the global scope in an async function.
For example:
// ...global imports...
new Promise (async () => {
// ...all code in the global scope...
}).then()

npm test doesn't catch errors thrown inside async function

I'm currently working on some tests scripts for my project. The code is written in ES7 and compiled using babel
First some background:
Let's say you run npm test and the test file looks like this:
function foo (bar) {
if (!bar) throw new Error('foooo')
console.log(bar)
}
foo() // No argument given
Now when you run this script the npm will show you an error like this:
$ npm ERR! Test failed. See above for more details.
This is the desired output, something went wrong, you do not want to release this software yet since there's an error in there.
So what's my question?
Okey so I have a test script setup like this:
async function init () {
const status = await someRequestPromise()
if (status.error) throw status.error
console.log(status.result) // Log the result of this async request...
}
init()
To run my test first I compile the ES7 using babel like so:
$ babel src -d dist
and then run npm test
The result
To be clear, npm did not trigger the error...
I also tried the following code, however this did not change anything...
async function init () {
const status = await someRequestPromise()
if (status.error) throw status.error
console.log(status.result) // Log the result of this async request...
}
try {
init()
} catch (e) {
console.log(e)
}
I'd like to know how I can trigger npm to catch the error and report the fact that the test failed. Also I'd like to know how to catch the error's, since node marks them as unhandled right now, even with the try catch block.
PS. adding an await statement in front of the init() function causes a syntax error, please don't suggest that.
since async/await returns promises, you must handle rejections (just as the console output suggests) via .catch:
const handle = init()
handle
.then((result) => handleSuccess(result))
.catch(({message}) => console.log(message))
try/catch (at least, in my experience)is for sync code.
EDIT:
To make the script 'crash', You may just add a process.exit(1).
const handle = init()
handle
.then((result) => handleSuccess(result))
.catch(({message}) => {
console.log(message)
process.exit(1)
})
Years late, but I believe one correct way to do this (without top level await) is to move the try catch into the 'init' function
async function init () {
// Try / catch with async / await is basically syntactic sugar for resolve() / reject()
try {
// This async call MUST use await or the promise will not resolve here,
// and thus the try / catch wrapping the async call will not trigger
const status = await someRequestPromise()
if (status.error) {
// Throw to reject the promise returned from init()
throw status.error
}
// Log the success of this async request
console.log(status.result)
// Catch your error to do logging.
} catch (e) {
// Log the failure of this async request
console.log(e)
// Rethrow to return the promise as rejected (with result as the error object)
// If you do not rethrow, the promise from init() will return as resolved
throw e
}
// Return the promise from init() as fulfilled (with result as void)
}
// Use a thenable to initiate the promise chain at the top level
init().then();
The comments might not be 100% accurate, but it's trying to get the idea across.
Basically a try catch can be used to resolve or reject promises, but only if the promises are awaited somewhere within the try/catch

Using await outside of an async function

I was attempting to chain two async functions together, because the first had a conditional return parameter that caused the second to either run, or exit the module. However, I've found odd behavior I can't find in the specs.
async function isInLobby() {
//promise.all([chained methods here])
let exit = false;
if (someCondition) exit = true;
}
This is a bastardized snippet of my code (you can see the full scope here), that simply checks if a player if already in a lobby, but that's irrelevant.
Next we have this async function.
async function countPlayer() {
const keyLength = await scardAsync(game);
return keyLength;
}
This function doesn't need to run if exit === true.
I tried to do
const inLobby = await isInLobby();
This I hoped would await to results, so I can use inLobby to conditionally run countPlayer, however I received a typeerror with no specific details.
Why can't you await an async function outside of the scope of the function? I know it's a sugar promise, so it must be chained to then but why is it that in countPlayer I can await another promise, but outside, I can't await isInLobby?
There is always this of course:
(async () => {
await ...
// all of the script....
})();
// nothing else
This makes a quick function with async where you can use await. It saves you the need to make an async function which is great! //credits Silve2611
Top level await is not supported. There are a few discussions by the standards committee on why this is, such as this Github issue.
There's also a thinkpiece on Github about why top level await is a bad idea. Specifically he suggests that if you have code like this:
// data.js
const data = await fetch( '/data.json' );
export default data;
Now any file that imports data.js won't execute until the fetch completes, so all of your module loading is now blocked. This makes it very difficult to reason about app module order, since we're used to top level Javascript executing synchronously and predictably. If this were allowed, knowing when a function gets defined becomes tricky.
My perspective is that it's bad practice for your module to have side effects simply by loading it. That means any consumer of your module will get side effects simply by requiring your module. This badly limits where your module can be used. A top level await probably means you're reading from some API or calling to some service at load time. Instead you should just export async functions that consumers can use at their own pace.
As of Node.js 14.3.0, the top-level await is supported with a flag:
--experimental-top-level-await
As of Node.js 16.12.0 / 17.0.0, no flag required.
Further details: https://v8.dev/features/top-level-await.
you can do top level await since typescript 3.8
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await
From the post:
This is because previously in JavaScript (along with most other languages with a similar feature), await was only allowed within the body of an async function. However, with top-level await, we can use await at the top level of a module.
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);
// Make sure we're a module
export {};
Note thereโ€™s a subtlety: top-level await only works at the top level of a module, and files are only considered modules when TypeScript finds an import or an export. In some basic cases, you might need to write out export {} as some boilerplate to make sure of this.
Top level await may not work in all environments where you might expect at this point. Currently, you can only use top level await when the target compiler option is es2017 or above, and module is esnext or system. Support within several environments and bundlers may be limited or may require enabling experimental support.

Categories

Resources