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! :)