Exercise to fetch data from JSON files using ES6 promises I did an exercise (self-imposed) promise fetching some data from placeholder JSON files on https://jsonplaceholder.typicode.com/. The exercise in my opinion is finished, but I can't shake the feeling that the code is as dirty as code can be. Please check it out and point me into the right direction and tell me what I did wrong.

The exercise can be found here: http://codepen.io/kresimircoko/pen/vggPmJ

'use strict';
const root = 'https://jsonplaceholder.typicode.com/';
const postList = document.querySelector('#post-list');
console.clear();

const commentsPromise = new Promise((resolve, reject) =&gt; {
    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        resolve(this.response);
    };
    xhr.onerror = reject;
    xhr.open('GET', root + 'comments');
    xhr.send();
});

const usersPromise = new Promise((resolve, reject) =&gt; {
    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        resolve(this.response);
    };
    xhr.onerror = reject;
    xhr.open('GET', root + 'users');
    xhr.send();
});

const postsPromise = new Promise((resolve, reject) =&gt; {
    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        resolve(this.response);
    };
    xhr.onerror = reject;
    xhr.open('GET', root + 'posts');
    xhr.send();
});

// Posts
postsPromise
    .then(data =&gt; {
        const posts = JSON.parse(data); // Contains our posts

        usersPromise
            .then(data =&gt; {
                const users = JSON.parse(data); // Contains our users
                commentsPromise
                    .then(data =&gt; {
                        const comments = JSON.parse(data);
                        displayPosts(posts, users, comments);
                    });
            });
    }).catch(error =&gt; { console.log(error); });

// Users

function displayPosts(posts ,users, comments) {
    let output = '';
    posts.map(post =&gt; {
        output += `
            &lt;li class="post"&gt; 
                &lt;h4&gt; ${displayUserName(users, post)} &lt;/h4&gt;
                &lt;h3&gt; ${post.title} &lt;/h3&gt;
                &lt;p&gt; ${post.body} &lt;/p&gt;
                &lt;div class="comment-count"&gt;&lt;span&gt; ${displayCommentAmount(comments, post)} comments&lt;/span&gt;&lt;/div&gt;
            &lt;/li&gt;
        `;
    });
    postList.innerHTML = output;
}

function displayUserName(users, post) {
    return users.map(user =&gt; {
        if (user.id === post.userId) {
            return user.name;
        }
    }).join('');
};

function displayCommentAmount(comments, post) {
     const commentArray = comments.filter(comment =&gt; {
        return comment.postId === post.id;
    });
    return commentArray.length;
}
There are lots of things you can do:


Use a parameterized function to create the XHR promises, since your three functions are identical other than what they're requesting
Use Promise.all, since none of the requests relies on information from previous requests and they can run in parallel
Move the JSON parsing into the functions retrieving the data, rather than making the consumer of them do it


And a bugfix, I think: The response text is this.responseText, not this.response, in the XHR load callback.

That would let us replace all of this:

const commentsPromise = new Promise((resolve, reject) =&gt; {
    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        resolve(this.response);
    };
    xhr.onerror = reject;
    xhr.open('GET', root + 'comments');
    xhr.send();
});

const usersPromise = new Promise((resolve, reject) =&gt; {
    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        resolve(this.response);
    };
    xhr.onerror = reject;
    xhr.open('GET', root + 'users');
    xhr.send();
});

const postsPromise = new Promise((resolve, reject) =&gt; {
    const xhr = new XMLHttpRequest();
    xhr.onload = function() {
        resolve(this.response);
    };
    xhr.onerror = reject;
    xhr.open('GET', root + 'posts');
    xhr.send();
});

// Posts
postsPromise
    .then(data =&gt; {
        const posts = JSON.parse(data); // Contains our posts

        usersPromise
            .then(data =&gt; {
                const users = JSON.parse(data); // Contains our users
                commentsPromise
                    .then(data =&gt; {
                        const comments = JSON.parse(data);
                        displayPosts(posts, users, comments);
                    });
            });
    }).catch(error =&gt; { console.log(error); });


with this:

const requestJSON = objType =&gt; {
    return new Promise((resolve, reject) =&gt; {
        const xhr = new XMLHttpRequest();
        xhr.onload = function() {
            try {
                resolve(JSON.parse(this.responseText));
            }
            catch (e) {
                reject(e);
            }
        };
        xhr.onerror = reject;
        xhr.open('GET', root + objType);
        xhr.send();
    });
};

Promise.all([requestJSON('posts'), requestJSON('users'), requestJSON('comments')])
    .then(results =&gt; {
        displayPosts(...results);
    })
    .catch(error =&gt; {
        console.log(error);
    });


So shorter, simpler, and more centralized for debugging, not repeating yourself, etc.

If you want the requests done in series instead of in parallel even though they don't have to be, just change the Promise.all part to:

requestJSON('posts')
    .then(posts =&gt; requestJSON('users')
        .then(users =&gt; requestJSON('comments')
            .then(comments =&gt; {
                displayPosts(posts, users, comments);
            })
        )
    )
    .catch(error =&gt; {
        console.log(error);
    });


(I tried to keep that in line with your line break/indentation style...)

Final result (with parallel requests):

'use strict';
const root = 'https://jsonplaceholder.typicode.com/';
const postList = document.querySelector('#post-list');
console.clear();

const requestJSON = objType =&gt; {
    return new Promise((resolve, reject) =&gt; {
        const xhr = new XMLHttpRequest();
        xhr.onload = function() {
            try {
                resolve(JSON.parse(this.responseText));
            }
            catch (e) {
                reject(e);
            }
        };
        xhr.onerror = reject;
        xhr.open('GET', root + objType);
        xhr.send();
    });
};

Promise.all([requestJSON('posts'), requestJSON('users'), requestJSON('comments')])
    .then(results =&gt; {
        displayPosts(...results);
    })
    .catch(error =&gt; {
        console.log(error);
    });

// No changes from here on out...

function displayPosts(posts ,users, comments) {
    let output = '';
    posts.map(post =&gt; {
        output += `
            &lt;li class="post"&gt; 
                &lt;h4&gt; ${displayUserName(users, post)} &lt;/h4&gt;
                &lt;h3&gt; ${post.title} &lt;/h3&gt;
                &lt;p&gt; ${post.body} &lt;/p&gt;
                &lt;div class="comment-count"&gt;&lt;span&gt; ${displayCommentAmount(comments, post)} comments&lt;/span&gt;&lt;/div&gt;
            &lt;/li&gt;
        `;
    });
    postList.innerHTML = output;
}

function displayUserName(users, post) {
    return users.map(user =&gt; {
        if (user.id === post.userId) {
            return user.name;
        }
    }).join('');
};

function displayCommentAmount(comments, post) {
     const commentArray = comments.filter(comment =&gt; {
        return comment.postId === post.id;
    });
    return commentArray.length;
}