My code is:
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', function (err, text) {
console.log(text);
await sleep(5);
});
}
run();
await sleep(1)
is fine, but await sleep(5)
results in:
SyntaxError: async is only valid in async function
Is fs.readFile
the problem? How do I use await
in such a situation?
The above is a reduced example to test. In my actual usage, I need to put the await sleep
in a callback deeply nested inside a few more asynchronous functions.
I'm not sure what sleep
is doing, but the problem in your question is happening because the callback to fs.readfile
is not an async function and the that is where await sleep(5)
is.
async function run() {
await sleep(1);
fs.readFile('list.txt', 'utf8', function (err, text) {
console.log(text); /* ^this isn't an async function */
await sleep(5);
});
}
You could make that error go away by passing an async function as a callback, however mixing promise and callback flows is not recommended and leads to problems, especially when handling errors. It prevents you from chaining the promise and catching errors from the inner await outside of run()
.
A better approach is to wrap fs.readFile()
in a promise and await that.
async function run() {
await sleep(1);
const text = await new Promise((resolve, reject) => {
fs.readFile('list.txt', 'utf8', function (err, text) {
if (err) reject(err) else resolve(text);
});
});
console.log(text);
await sleep(5);
}
This will allow you to catch any errors in much more robust way with:
run()
.then(() => /*...*/)
.catch(err => /* handle error */
and avoid unhandled rejections.
The above is a reduced example to test. In my actual usage, I need to put the await sleep in a callback deeply nested inside a few more asynchronous functions.
Callback based APIs shouldn't be augmented with async..await
or promises in general when there is a chance to stick to promises (may not be possible if a callback is called more than once). This results in poor control flow and error handling.
Once there is async
callback function inside run
, it's impossible to chain nested promise with run().then(...)
. Errors from nested promises may remain unhandled as well and result in UnhandledPromiseRejectionWarning
.
The proper way is to move from callbacks to promises and use promise control flow. Generally this can be achieved with promise constructor:
async function run() {
await sleep(1);
const text = await new Promise((resolve, reject) => {
fs.readFile('list.txt', 'utf8', function (err, text) {
if (err) reject(err) else resolve(text);
});
});
console.log(text);
await sleep(5);
}
There are numerous ways to get promises from Node callback based APIs, e.g. fs
provides promise API since Node 10:
async function run() {
await sleep(1);
const text = await fs.promises.readFile('list.txt', 'utf8');
console.log(text);
await sleep(5);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With