Reputation: 165
I am new to NodeJS and I am finding it slightly hard to work with asynchronous functions. I am trying to figure out the benefits here. Below are my understandings on Asynchronous functions.
Can you kindly please validate each of my understandings below?
Upvotes: 3
Views: 2209
Reputation: 4656
The actual use of async functionality in a javascript/node application comes into play when dealing with network requests and/or I/O operations (or things that can take a long time).
A Network Call Example
Take a chat application for example. The client (you) send a message to some person A. The message is sent to the server or directly to the recipient(p2p) for further processing according to the communication model. You also want to send another message to some other person; person B. But if the requests are not treated asynchronously, the client (you) could have to wait for an indeterminate period of time; depending upon your network speed, the recipient's network speed, the network timeout period or any other factors responsible.
Consider you have to wait for 2 units of time to finish your request
time: 1 2 3 4 5
action: SendToA(message) waitA waitA ResponseFromA SendToB(message)
But if the same request is asynchronous. You send the request that is treated asynchronously by the client (for p2p communication) or by the client and the server both (for client/server) communications. This is what actually happens in the browsers. The browsers treat requests asynchronously, so that any other portion of your browser code doesn't get stuck while you wait for your request to finish.
time: 1 2 3 4 5
action: SendToA(message) waitA/SendToB(message) waitA/waitB ResponseFromA ResponseFromB
So in the sync case, you get only one request to succeed within 5 time units, while in async case you get two requests to succeed within 5 time units.
note : Real implementations may defer in time units, this is just a general idea of what actually happens.
An I/O read/write Example
I/O read/write is very similar to network call; i.e. it takes time to finish. Say your app has two tables that read from two different files and populate each one. If you treat both requests synchronously, you get a massive delay in your overall execution, while async helps.
For sync:
time: 1 2 3 4 5
action: ReadFromFileA(message) waitA waitA ResponseFromA ReadFromFileB(message)
For async:
time: 1 2 3 4 5
action: ReadFromFileA() ReadFromFileB() waitA/waitB ResponseFromA ResponseFromB
This was a very general explanation of what async calls/functions can do. However, async is not always the thing to do. As you have mentioned in your question, there are times when you do need synchronous calls/functions.
Printing is a very peculiar action that has to be synchronous. You can not print two papers asynchronously.
Dependent functions must also be sync.
All this said, async is a very powerful feature if used correctly.There are (almost)always possibilities for asynchronous-ness in a real-time app. If your app doesn't use it, your user might complain about bad UX in your app.
Upvotes: 1
Reputation: 2626
Alright, let's look into it piece by piece. Just as a base, remember that Node.js is a single threaded process and if it needs to execute blocking processes
such as reading a file (create an event pointer, read the file handle, set file path, set open mode, etc.) it's better to use async functions which execute on a separate thread of the same page or thread s/pool.
1) Asynchronous functions are good to use when an independent operation (from the main program flow) to be executed. It is not ideal to use asynchronous functions when the data/response from the asynchronous function is very much required by the main program or when the various independent asynchronous functions are interconnected.
For starters, we will not refer to the program file as the main program
since there are no sub-programs in the Node.js world (I am not talking about modules and the likes).
Now, you are actually right on the money when you say that you shouldn't use any async functions when there is an immediate need of the output. Let's take the following example:
...
const data = readFile( 'fileName', ( data, output ) => {
...
} );
console.log( data ); // this might be null or undefined
In the above case, we will not use async function (in the traditional sense). However, with ES6 and above, we get the lovely async/await
paradigm:
const data = await readFile( 'filename' );
await
makes the call pseudo-sync: it behaves like an async
function, but there will be a paused thread to wait for the output. So, here, you are absolutely right! Let's move on.
2) It is not good to depend on the result of the output of the async function in the main program flow. Because Async always executes after the main flow. So if you need some functions to be executed in the main flow, its good to define it as Synchronous, not asynchronous.
Here, you say that async
operates after the main flow. Now, that is incorrect. Let me draw a simple picture of thread evaluations and execution:
Say, there are two sync
functions A()
and B()
, and their corresponding threads are th__A
and th__B
, they will go something like this:
th__a ---> th__b
If they are fired in the order A()
then B()
. It waits for the evaluation of the first sync (or blocking) process and then executes the second one. As is evident, it is NOT after the entire execution ends.
However, if they are now async functions, they will execute in parallel. Say A()
was a sync function and B()
was an async function with the same thread names as above, the execution is something like this:
th__a ----
- - th__b ->
Where -
represents a clock cycle and ->
represents the end of execution. We can see that A()
is fired and then, on a new thread, B()
is fired.
I guess this makes sense. Now, coming back, again if you need to use them immediately while having then as async calls, use await
.
3) When Independent async functions are called, it's a common practice to call the subsequent operation(async function), using promises or callbacks.
Absolutely.
Say, we define a function sayHello()
:
const sayHello = () => {
const P = Q.defer();
// P.resolve(data);
// or if there is an exception
// P.reject(error);
return p.promise;
};
where Q
is the excellent promise library. We can call it like:
sayHello.then( ( data ) => {
console.log( data ); // P.resolve(..) is working here since the promise was successful.
} ).catch( ( err ) => {
console.log( err ); // P.reject(..) is working here since there was a problem.
} );
Or you can use call backs like fs.readFile(...)
:
fs.readFile( 'fileName', ( e, data ) => {
if( e ) { return console.log( e ); } // error was handled
// perform subsequent functions here with data
} );
4) I still can call a sync function within an Async function, but the program may not work as expected if an Async function is called from a sync function/operation since the async function will be executed only at the last?
Not really. Refer to point (2). It's about threads. Not static processes. You can very well call a sync function within an async function and it'll work perfectly.
When you do read file, say you want to split the data by \n
or new line:
...
if( e ) { return console.log( e ); }
const dataLines = e.split( '\n' ); // this will work brilliantly
...
I hope this made everything clear! :)
Upvotes: 5
Reputation: 2918
It seems like you haven't studied about the javascript event loop yet. Here is a good article that you can follow. Usually in javascript async function is used to avoid IO (Input/Output) delay on a process like a HTTP call on browser, File read on NodeJS, not the execution delay or paralle execution, since a javascript process is single threaded. If you want to study more about Non blocking IO, Here is an article that I've written about how it works, that seem to answer your all of questions.
When writing asynchronous methods, as you mentioned it is hard to follow the each callback then execute the again a call back, then again and again which is called a callback hell in javascript. await
async
is not more than a simple solution for that.
Upvotes: 1