Milan Kitic
Milan Kitic

Reputation: 33

Vanilla JS nested fetch with youtube data API

I've just started learning JS a week ago so I'm pretty new to everything in there, and for my first project I have to write in Vanilla JS. The project is to make a fake YouTube app using YouTube Data API.

Here's the code that I'm having a problem with (I want to continue with rest of the code only after the data2 function is finnished):

function generateResults(data){
        pageTokens.push(data.nextPageToken);
        data.items.forEach((item) => {
            let thumb = item.snippet.thumbnails.medium.url;
            let title = item.snippet.title.substring(0, 60);
            let channel = item.snippet.channelTitle;
            let vid = item.id.videoId;
            let viewCount = 0;

            const data2 = async () => {
                const response = await fetch (`https://www.googleapis.com/youtube/v3/videos?id=${vid}&key=${API_KEY}
                &part=statistics`)
                const json = await response.json();
                viewCount = json.items[0].statistics.viewCount;
            }
            data2();
            
            videosHTML += `
            <article class="art" data-key="${vid}">
                <img src="${thumb}" alt="" class="thumb">
                <div class="details">
                    <h4>${title}</h4>
                    <p>${channel}</p>
                    <p>${viewCount} - Date</p>
                </div>
            </article>
            `;
        })
        document.getElementById('videos').innerHTML = videosHTML;
        videosHTML="";
}

In this part I'm making video thumbnails and I'm using 2 API's to do so. With the first API (which is not included in this code) I'm getting the search list of all the videos, and then in this function (generateResults) I'm using that fetched data to call the second API to give me specific information on each video (I need the view count). But because fetch is async the viewCount doesn't get included and it stays 0. I've tried to make generateResults and the arrow function in the forEach loop async as well but then innerHTML part doesn't execute at all and I'm getting no thumbnails at all.

As I've said I'm pretty new to all of this so sorry if it's something really obvious but I've spent 10+ hrs searching for answers on the internet and nothing works. Ty in advance!

P.S.: The data2 function is working, I've tried console.log("viewCount") and it properly displays the viewCount but it probably only executes after the innerHTML part is already executed.

Upvotes: 3

Views: 818

Answers (2)

Obzi
Obzi

Reputation: 2390

Another answer

function generateResults(data){
    pageTokens.push(data.nextPageToken);
    data.items.forEach((item) => {
        let thumb = item.snippet.thumbnails.medium.url;
        let title = item.snippet.title.substring(0, 60);
        let channel = item.snippet.channelTitle;
        let vid = item.id.videoId;
        let viewCount = 0;

        const data2 = async () => {
            const response = await fetch (`https://www.googleapis.com/youtube/v3/videos?id=${vid}&key=${API_KEY}
            &part=statistics`)
            const json = await response.json();
            viewCount = json.items[0].statistics.viewCount;
            
            document.querySelector(`#videos article[data-key="${vid}"] .view-count`).innerText = viewCount;
        }
        data2();
        
        
        videosHTML += `
        <article class="art" data-key="${vid}">
            <img src="${thumb}" alt="" class="thumb">
            <div class="details">
                <h4>${title}</h4>
                <p>${channel}</p>
                <p><span class="view-count">Loading</span> - Date</p>
            </div>
        </article>
        `;
    })
    document.getElementById('videos').innerHTML = videosHTML;
    videosHTML="";
}

Upvotes: 2

Bruno Monteiro
Bruno Monteiro

Reputation: 4519

You are in the right direction. You can move the code that generates your HTML (videosHTML) inside your data2 function, so it will wait for your async call to return.

You can also use append to add more HTML content to your videos div, so you don't need to keep cleaning the videosHTML variable. In fact, you can create that variable inside your data2 function.

Here is the updated code:

function generateResults(data){
  pageTokens.push(data.nextPageToken);
  data.items.forEach((item) => {
    let thumb = item.snippet.thumbnails.medium.url;
    let title = item.snippet.title.substring(0, 60);
    let channel = item.snippet.channelTitle;
    let vid = item.id.videoId;
    let viewCount = 0;

    const data2 = async () => {
        const response = await fetch (`https://www.googleapis.com/youtube/v3/videos?id=${vid}&key=${API_KEY}
        &part=statistics`)
        const json = await response.json();
        viewCount = json.items[0].statistics.viewCount;

        const videosHTML = `
        <article class="art" data-key="${vid}">
            <img src="${thumb}" alt="" class="thumb">
            <div class="details">
                <h4>${title}</h4>
                <p>${channel}</p>
                <p>${viewCount} - Date</p>
            </div>
        </article>
        `;
        document.getElementById('videos').append(videosHTML);
    }
    data2();
  })
}

Upvotes: 0

Related Questions