Hi there,
Many times while writing NodeJS, the code becomes so messy due to its async nature and we have to face problems like callback hell. I know how to use Promises and async/await but at times code written using that is also not so clean.
I need some tips on to how to write my NodeJS async code neatly. Are there node modules which helps achieve the task?
Since most browsers now support the latest ES6 (and above) features natively (along with Node.js, as well), the best way is to use async/await . The code you get is really clean and readable.
Let's take a look at some examples.
The inevitable truth about programming is that the speed at which new features are released vs. the speed at which they are incorporated into old packages, modules, or codebase doesn't match. Ever. For that reason, we do have a lot of packages on npm which still use the old callback(err, data) approach. However, there is a pretty simple way to turn those into async/await-compliant functions:
const promisify = function() {
const [ fn, ...args ] = Array.from( arguments );
return new Promise( ( resolve, reject ) => {
fn( args, ( err, data ) {
if ( err ) { reject( err ); }
else { resolve( data ); }
}
}
};
Now, of course, that is a very basic example (read: do not use this in production); but you get the point.
With that, suppose we have the following function:
const doStuff = ( param1, cb ) => {
setTimeout( () => {
cb( `The data is ${ param1 }` );
}, 2000 );
};
We can get a promise by doing:
const doStuffPromisify = promisify( doStuff, 'Hello' );
async/await is, in a way, a wrapper around promises. Suppose we have the following function:
const doStuffPromises = param => {
return new Promise( ( resolve, reject ) => {
setTimeout( () => {
resolve( `The data is ${ param }` );
}, 2000 );
}
};
I won't go into the way of using this using the .then()/.catch() blocks; let's use async:
const executeFunction = async () => {
try {
const data = await doStuffPromises( 'Hello' );
console.log( data );
} catch (error) { console.error( error ); }
}
executeFunction();
The try block acts like the .then() chain; if we convert this to the .then() chain, it'll look like this:
doStuffPromises( 'hello' ).then( console.log ).catch( console.error );
Not convinced? Let's chain it.
doStuffPromises( 'hello' )
.then( doStuffPromisify )
.then( console.log )
.catch( console.error );
Or:
const executeFunction = async () => {
try {
const data = await doStuffPromises( 'Hello' );
const data2 = await doStuffPromisify( data );
console.log( data2 );
} catch (error) { console.error( error ); }
}
executeFunction();
A lot simpler, right?
It's always better to use a bit of the native .then() chain if the code is as simple as above. However, with growing complexity, you might want to use the try/catch with async/await.
Girish Patil
Full-stack engineer
As Shreyansh Pandey suggeseted chain function calls when you are using
.then()ex. lets say I have a list to be fetched, filter on variety of conditions, transform the results and then write the results to a file, you can maybe sturucture it into like this// try {} catch {} and throw if something goes wrong in these functions const fetchData = async function(){ // Get thanos for me } const filterData = function(data){ // do your thing here and return something } const transformData = function(data){ // do your thing here and return something } const writeToFile = async function(data){ // write the data to a file } fetchData() .then(filterData) .then(transformData) .then(writeToFile) .catch(err => console.log(err)); // ES6 // The same with async await async function main(){ try{ // You can make some tweaks here though. data = await fetchData(); filteredData = filterData(data); transformedData = transformData(filteredData) await writeToFile(transformedData); } catch(err => { console.log(err) }) } main()This gives anyone looking into the code a clear picture of whats going on. Its always better to stick to one convention. If you are using .then() try to make it same through out the codebase. If you are using
async/awaitwrap it in try/catch follow the same. But in situations where you face long chaining I suggest its better to go withthenas you can see its easier to understand and clean.Along with that use prettier prettier.io or code formatters to maintain a same visual structure through out projects or any linters to keep your code clean.