Let's spend a bit more time on the returning of values from asynchronous functions.
In the last lesson, we learned that asynchronous functions never return the result of an asynchronous operation:
import fs from 'fs';
const noop = () => {};
const content = fs.readFile('./myfile', 'utf-8', noop);
console.log(content); // undefined
And the only way to get a result is to describe the logic in the callback. Then we should ask what happens if we make a return
inside the callback. What will this lead to:
import fs from 'fs';
const content = fs.rea dFile('./myfile', 'utf-8', (_error, data) => {
// doing something
return data;
});
console.log(content); // undefined
As a result, nothing changes because this return isn't used by anything. It does not mean that the return
instruction is useless in asynchronous code.
On the contrary, it's helpful as a way to interrupt code execution and not return the result:
import fs from 'fs';
const content = fs.readFile('./myfile', 'utf-8', (_error, data) => {
if (data === '') {
return;
}
// doing something with the data
});
console.log(content); // undefined
This pattern is called guard expression.
All the information above applies to asynchronous functions which we wrote on our own.
An asynchronous function is any function that has at least one asynchronous operation inside it. Without exception. Even if in addition to the asynchronous operation, it also performs a synchronous operation, for example, text modification. So, every asynchronous function must take a callback as its only way of ordering events and tracking completion.
Let's write an asynchronous wrapper function to read the file, which, besides the reading itself, does a little cleaning by removing leading and trailing spaces from the content.
Since our function is asynchronous, it must take a callback function as an input, and that function will be called at the end of the operation.
This function should have the generally accepted signature, with the first parameter as an error and the second as the data itself. Our asynchronous function cannot return data using return
:
import fs from 'fs';
const readFileWithTrim = (filepath, cb) => {
fs.readFile(filepath, 'utf-8', (_error, data) => {
// Error handling hasn't been considered yet
// Pass null as the first parameter when calling a callback function
cb(null, data.trim());
})
}
readFileWithTrim('./myfile', (_error, data) => console.log(data));
This process is recursive, so any function that internally works with an asynchronous function becomes asynchronous and starts taking a callback function as the input.
Why does this happen? Why can't you perform an asynchronous operation internally without returning it outside the function in any way? In this case, you can't:
- Use the result of an asynchronous function because the data comes to the callback function in a different call stack
- Find out whether the operation has finished, whether it was successful
We'll look at all of this in future lessons.
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.