Definition

Synchronous and Asynchronous, now i'm sure you've heard of this terms at least once by now, if not and well let me break it down to you very simple  :

1 - Synchronous: Means that the compiler waits for each operation to complete, once it's done it executes the next operation and so on.

2 - Asynchronous : The compiler doesn't wait for each operation to complete, rather it executes all operations in the first go, then the result of each operation will be handled once it's available in a callback function.

An example of synchronous coulde would be :


console.log('First');
console.log('Second');
console.log('Third');

The output here is simple : First, Second and then Third. Each block is executed once the one before is done.

Meanwhile for asynchronous the execution of a code block might take a couple of seconds (for example fetching data from the database or an external API) 


console.log('First');
setTimeout(function(){ console.log('Second'); }, 3000);
console.log('Third')

The execution order as you guessed is: First, Third, Second

The issue here, is that we don't know how much time it would take to print 'Second', sure in this example we set it to 3 seconds, but in a real world application we can't control the time, it might take longer or less then 3 seconds, so how can we make sure to not print the 'Third' until we retrieved 'Second'.

Callbacks


function log_message(message, callback){
    setTimeout( () => {
        console.log(message);
callback();
},3000);
} log_message('First', function() { log_message('Second', function() { log_message('Third')
}) })

Great, now our code don't log a message until the previous one is logged, we have a synchronous behavior.

Although the Callbacks are doing great job here, but they're not easy to read especially if you have a much complex code structure, where you want to manipulate each result before fetching the next data, then we will be having what's called 'Callback Hell'  a bunch of nested code that's difficult to read.

Promises

Let's repeat the same code while using promises : 

function log_message(message, callback){
    return new Promise( (resolve, reject) => {
        setTimeout( () => {
            const error = false;
            if(!error) {
                console.log(message);
                resolve();
            } 
else { reject('Error : Something went wrong'); } }, 2000) })
} log_message('First') .then(() => log_message("Second") ) .then(() => log_message("Third") ) .catch(error => console.error(error))

Here we can see it's a lot cleaner and easier to read, instead of callback(), Promises uses "resolve" if the asynchronous operation was successful, and "reject" if there was an error. Also we added a catch method for error handling. Feel free to change the value of the "error" variable to simulate an error.

Async/Await

Async/Await is a language feature that use the same concept of Promise, the advantage is making the syntax and structure of your code look more like using standard synchronous functions.

The await expression causes async function execution to pause until a Promise is resolved or rejected.

async function log_messages() {
    try {
        await log_message("First")
        await log_message("Second")
        await log_message("Third")
    } 
catch (error) { console.error(error) } } log_messages()

As you can see we wrapped the log_message functions inside a global function log_messages that contain the async keyword, that's because we can’t use Await at the global level, it always needs a wrapper function. 

Now the code is cleaner and easy to read, plus we didn't have the change the main Promise function log_message().

Conclusion

That's was a simple example to get you up and going with Asynchronous functions, Callbacks, Promise or Async/Await use the one you like, the purpose is to have a code that's easy to maintain and understandable to you and the others.

Let me know in the comments what's your favorite approach/ method for using Asynchronous operations.