How to assign axios respond to variable? - javascript

In Nodejs v16.15.1, I can easily assign axios respond to a variable.
const clientlist = await axios.get('http://test.lab/api/getclients');
However, when I upgraded to latest Nodejs v19.6.0,
I keep getting error: await is only valid in async functions.
So, I tried to change the code as per below
var uid1 = 0;
async function getUID() {
let res = await axios.get('http://test.lab/api/?user=test');
let data = res.data;
return data;
}
// Call start
(async() => {
console.log('before start, the value is '+ uid1);
uid1 = await getUID1();
console.log('after start, the value is '+ uid1);
})();
//trying to use uid1 for the rest of the code
console.log('outside async, the value is '+ uid1);
// returned empty
However, I can only use axios respond within the async scope.
I cannot use it outside.
What can I do to make it work like it did in nodejs v16?

Node.js still supports top-level await, but (still) only in ECMAScript modules (i.e. not in the default, CommonJS, modules).
To make Node.js recognise the file as an ECMAScript module, either:
Add "type": "module" to your package.json file and continue using a .js file extension.
Rename the file to have a .mjs file extension
This question covers why your attempted workaround (with an IIFE) failed.

Related

How to make Javascript synchronous in the main execution context? [duplicate]

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() } )

js - why is await not throwing error when used outside function [duplicate]

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() } )

MongoDB / EJS: How to make synchronous query and render result values in EJS

I'm struggling a little bit with the concepts of asynchronous programming and I would appreciate some help/guidance from someone.
Basically, I'm developing a node.js web server that's connected to a mongodb database.
I'm using EJS to generate HTML files as you can see below.
app.get("/", function(req, res){
res.render('home', {date: getData.todaysDate(), n: getData.todaysIncidents(), nTot: getData.totalIncidents()});
});
Most of these values ('n' and 'nTot') are obtained by querying my database and then doing some other operations, as you can see in the following example code sample.
//------getData.js------//
exports.todaysIncidents = function() {
let server_n = 0;
Incident.find({dia: {$gt:y}}, function(err, result) {
if (err) {
console.log(err);
} else{
//do some stuff...
server_n = 12345
}
}
});
return server_n;
};
Here is the problem: The values printed in the HTML file are always those used for variable initialization, such as 0 for variable 'server_n'. After doing some research, I understand that this happens because .find(...) is an asynchronous function so the program executes right away the instruction "return server_n;", which means that on the HTML file the value showed will be 0 and not 12345.
I already looked at other questions here in StackOverflow, but I'm struggling to understand a possible fix to this issue, I mean I can't be the only one going through this, right?
Could you please provide some basic explanation of how I could go around this issue? I'm still learning and a lot of these concepts remain hard to understand.
Thank you very much.
Yes, you are right that the problem is as a result of improper handling of asynchronous operations like querying the DB. So how do you fix it?
Using async/await:
There are multiple options to handle asynchronous operations in NodeJS, however, I would strongly recommend using async/await, it's syntactically clean and easy to understand.
To put it simply, async/await is a way of specifying and dealing with asynchronous operations. You use the async keyword to specify that a function is asynchronous, and you use the await keyword to wait for an asynchronous operation. One key thing to note is that you can only use the await keyword inside an async function. You can read more about async/await here.
If your nodeJS version is 7.6 or higher, async/await is supported out of the box, however, if you are using a lower version and can't upgrade, you can set up build tools like Babel to be able to use javascript features supported in newer ECMAScript spec.
When using async/await, your code should be something like this.:
//------getData.js------//
// NOTE: the typeof todaysIncidents is not more the regular function,
// it's now an AsyncFunction because of the async keyword
exports.todaysIncidents = async function () {
let server_n = 0;
try {
// In simple terms, the await keyword here would ensure that the DB query
// resolves or reject before moving on to the next statement
const incident = await Incident.find({ dia: { $gt: y } });
// The incident variable stores the result of the query
server_n = 12345
} catch (err) {
// Handle error from the DB query
console.log(err);
}
return server_n;
};
.
//------The router------//
// NOTE: You also need to make the route handler an AsyncFunction
app.get("/", async function (req, res) {
// You can await the differeint DB queries in here to ensure they resolve(or reject) before rendering the view
const todaysDate = await getData.todaysDate();
const todaysIncidents = await getData.todaysIncidents();
const totalIncidents = await getData.totalIncidents();
res.render('home', { date: todaysDate, n: todaysIncidents, nTot: totalIncidents });
});

Return SVG from a function

I'm trying to return an SVG file located in the same folder as the Javascript file when a function is executed.
The function is called returnSVG, the SVG is called image.
import image from './image.svg'
returnSVG= () => {
return image;
}
But when I call it I get this: /static/media/image.f85cba53.svg
An SVG file contains a textual representation of a graphic, encoded in an XML-like syntax.
So, you cannot simply import thing from 'filepath' because the file contents are not a JavaScript module, a loadable binary extension, or a JSON-encoded string.
Rather than importing what cannot be imported, the correct approach would be to explicitly read the contents from the file:
const { readFileSync } = require('fs')
const returnSvg = (path = './image.svg') => return readFileSync(path)
Note that this example uses the synchronous version of the fs function to read a file. This will pause (block) all processing in NodeJS until the contents have been read.
The better solution would be to use Promises via async/await as in:
const { readFile } = require('fs')
const { promisify } = require('util')
const asyncReadFile = promisify(readFile)
const returnSvg = async (path = './image.svg') => {
const data = await asyncReadFile(path)
// since fs.readFile returns a buffer, we should probably convert it to a string.
return data.toString()
}
Using Promises and async/await requires that all of your processing occur within the chain of promises, so you'd need to restructure your code to:
returnSvg()
.then(data => { /* do something with the file contents here. */ })
.catch(err => console.error(`failed to read svg file: ${err}`))
Remember, in NodeJS, operations can be either asynchronous (via callbacks or Promises which async/await uses "under the covers"), or synchronous (via special "blocking" calls).
One option, if you choose to use the synchronous version would be to load all your files into your server before you call app.listen(8080, () => console.log('listing on 8080') but this implies you know what files will be required before you run your server and would not work for dynamically loaded files.
You might imagine doing something like:
const data = readSvg()
console.log(data)
or perhaps:
let data
readSvg().then(res => data = res)
console.log(data)
but neither will work because an asynchronous function (one defined with the async keyword) can only return a Promise.
Both attempts will not print any usable value to the console because at the time console.log() is called, NodeJS has no idea what the value of data will be.
The rule of thumb here is:
You cannot return a value or interact with any higher context from within a Promise.
Any manipulation of the data generated within a Promise chain, can ONLY be accessed from within its own chain.

Javascript / Nodejs use await on top level in nodejs module

I tried finding the solution to my problem, but couldnt find it, and was looking for some "best practice examples". I have a nodejs express application and my functions are split in files. For example I have this controller (oktacontroller.js):
var okta_api_key = <here some await data, getting from db>;
const OKTA_ORG_URL = '<here too>';
exports.createUser = async (req, res) => {
console.log(okta_api_key);
}
exports.getGroups = async (req, res) => {
console.log(okta_api_key);
}
In both exported functions (which are express routes) I need the var okta_api_key. I know I can get them by querying them in both functions, because they are async functions and I can use await there, but it feels dumb to query this every time (because it wont ever change).
How can I manage this? I know I can do this:
var okta_api_key;
(async () => {
okta_api_key = await <getting key async>
})()
But this feels off as well..
Is there any way too make some sort of large function, which is async, and exports the two functions? In other words: How do I use await on the top level of a file (module). It doesnt need to be on top level (its impossible), but some sort of method to have my "top level" variables exposed to my exported functions.
EDIT: Some other usecase, because I got the suggestion of putting it in a config file. Yes, for this one it is possible, but for example: I have some other api key which gets his access token from the service itself, on every startup (because it expires). That token cannot be stored in the config file, so I need some async work to get the value. I know top-level-await is not working (or even not desirable), but I just want an example of how you guys would do this if it were your project :)
You are close:
var okta_api_key = (async () => {
return await <getting key async>
})();
Create a promise, then await that promise whenever you want to use it.
How do I use await on the top level of a file (module).
Top level await might look great: You just add one await and then you can access the variable synchronously. But that simplifies things too much: It will stop all modules depending on that module from executing. In most cases you don't want that¹. Instead create a promise of the async task, then await it when needed. That way you limit the asynchronous execution to the code pieces that actually need it.
¹ Those rare cases are:
1) Loading some global config, that you have to access everywhere in your code, so it makes no sense to start the service if the config isn't ready.
2) awaiting in the top level file of your service: As no module depends on it, this won't cause any problems.
Side note: Top level await is not yet specified, and the NodeJS support is also not there yet. To use it in production you have to wait a few months (?).

Categories