focused programmer writing code
Back view of focused young programmer in glasses writing code and eating pizza

How to Resolve Synchronous and Asynchronous Problems in JavaScript?

To understand callbacks, promises and async await we need to establish why we use them? JavaScript is a synchronous language. This means our code runs line by line. If one part of JavaScript is left behind for any reason then interpreter or transpiler will never come back to it and execute it or it will not wait for its execution. This is because JS run on single thread. 

const posts = [
    {title: 'Post One', body: 'This is my first post'},
    {title: 'Post Two', body: 'This is my second post'}
];

function getPosts(){
    let output = '';
    posts.forEach((post)=>{
        output += `<li>${post.title}</li>`;
    });
    document.body.innerHTML = output;
}
function createPost(post){
    posts.push(post);
}
createPost({title: 'Post three', body: 'This is the third post'})
getPosts()

In this code we are creating an array and then we write a function to show our posts on the page. See the tutorial below as we have written the function and to show the posts after creating an array. After that first we created a post and then we displayed it on the page. Everything will work as expected and we will see 3 posts on the page. Now we will simulate the live server experience by introducing a time delay in our code. We will add a 100ms delay in the getPost() method as if it took 100ms to get posts from the server and we will add 200ms in creating the post.

const posts = [
    {title: 'Post One', body: 'This is my first post'},
    {title: 'Post Two', body: 'This is my second post'}
];
function getPosts(){
    setTimeout( () => {
        let output = '';
        posts.forEach((post)=>{
            output += `<li>${post.title}</li>`;
        });
        document.body.innerHTML = output;
    }, 1000);
}
function createPost(post){
    setTimeout( () => {
        posts.push(post);
    } , 2000)
}
createPost({title: 'Post three', body: 'This is the third post'})
getPosts()

Now we will observe a strange thing. It will only show 2 posts. Why? Because the createPost() method took too long and it was skipped. On the live server it will show up more weirdly. Like sometimes code will execute as expected but sometimes it will skip the createPost() function.

It depends on how much execution time it will take. This means we have to devise a strategy to make sure that getPosts() only calls in order. It means it should be called after the createPost() method and it should show 3 posts. First way to implement it is callbacks. 

const posts = [
    {title: 'Post One', body: 'This is my first post'},
    {title: 'Post Two', body: 'This is my second post'}
];

function getPosts(){
    setTimeout( () => {
        let output = '';
        posts.forEach((post)=>{
            output += `<li>${post.title}</li>`;
        });
        document.body.innerHTML = output;
    }, 1000);
}

function createPost(post, callback){
    setTimeout( () => {
        posts.push(post);
        callback()
    } , 2000)
}

createPost({title: 'Post three', body: 'This is the third post'}, getPosts)

Here we passed a function to the createPost(post, callback) method and then we called it inside the function after pushing the post in the array. This always shows 3 posts no matter what. 

There is another way to implement the same behaviour. It is called Promises. 

const posts = [
    {title: 'Post One', body: 'This is my first post'},
    {title: 'Post Two', body: 'This is my second post'}
];

function getPosts(){
    setTimeout( () => {
        let output = '';
        posts.forEach((post)=>{
            output += `<li>${post.title}</li>`;
        });
        document.body.innerHTML = output;
    }, 1000);
}

function createPost(post){
    
    return new Promise((resolve, reject) => {
        setTimeout( () => {
        posts.push(post)
        
        let error = false
        if(!error){
            resolve()
        }
        else{
            reject('Message: Something went wrong.')
        }
        
    } , 2000)
    })
}

createPost({title: 'Post three', body:'This is the third post.'}).then(getPosts).catch(err => console.log(err))

Here we are creating a new promise in the createPost() method and once it’s resolved then we are calling the getPosts() method or we are showing an error message if the Promise gets rejected. This is the modern way to implement asynchronous code in JS.

Most API implement promises so you do not need to create them. Rather you will be using .then() and .catch() methods. There is another way to call promises. It will allow you to call multiple promises at once.

const createPostPromise = createPost({title: 'Post three', body:'This is the third post.'})

Promise.all([createPostPromise]).then(values => {getPosts(); console.log(values);}).catch(err => console.log(err))
Just to show you I will create more Promises and then I will resolve them all at once. 
const promise1 = Promise.resolve('Hello Promise')
const promise2 = 10
const promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'Wait until this promise is resolved.')
})
const createPostPromise = createPost({title: 'Post three', body:'This is the third post.'})

Promise.all([createPostPromise, promise1,promise3, promise2]).then(values => {getPosts(); console.log(values);}).catch(err => console.log(err))

Now promise3 will take one second to resolve but you will see that code will wait until every promise is resolved or rejected, no matter how long it will take. There is a drawback with this method too. If one of the promises will be rejected then no promise will be resolved. 

There is another very elegant and modern way to resolve synchronous problems in JavaScript.

const posts = [
    {title: 'Post One', body: 'This is my first post'},
    {title: 'Post Two', body: 'This is my second post'}
];

function getPosts(){
    setTimeout( () => {
        let output = '';
        posts.forEach((post)=>{
            output += `<li>${post.title}</li>`;
        });
        document.body.innerHTML = output;
    }, 1000);
}

const createPost = (post) => {

    return new Promise((resolve, reject) => {

        setTimeout( () => {

            posts.push(post);

            const error = false;

            if(!error){
                resolve();
            }
            else {
                reject('something went wrong!');
            }

        } , 2000)
    })

}

async function init(){
    await createPost({title: 'Post three', body: 'This is my third post.'});
    getPosts();
}

init()

First createPost() method will wait and get resolved then getPosts() method will be executed. Await will only work if we have implemented a promise in the method. Otherwise it will not wait. Here is a real world example of implementing async await. 

const init = async () => {
    const res = await fetch('https://jsonplaceholder.typicode.com/users');
    const data = await res.json();

    console.log(data);
}

init();