Search code examples
javascriptnode.jsfunctionglobal-variables

Can't get rid of a global variable (Need to make it non-global)


I'm working on a web app that shows the total duration of a playlist. Here we're dealing with the YouTube API. And I want to know how should I get rid of the global variable newPageToken. Also I still need to use it in the third function on this snippet.

let newPageToken = null;

// Next page for more results (Max 50 per page)
function getNextTokenURL() {
    console.log(newPageToken);
    return newPageToken
        ? `${playlistItemsURL}&playlistId=${extractedPlaylistIDId}&pageToken=${newPageToken}&key=${API_KEY}`
        : `${playlistItemsURL}&playlistId=${extractedPlaylistIDId}&key=${API_KEY}`;
}



async function getVideoIdsForPageToken() {
    try {
        const { data } = await axios.get(getNextTokenURL());
        const nextPageToken = data.nextPageToken;
        const videoIds = data.items.map((video) => {
            return video.contentDetails.videoId;
        });
        return { videoIds, nextPageToken };
    } catch (e) {
        if (e.response) {
            const { code, message } = e.response.data.error;
            throw new Error(`StatusCode ${code}. Reason: ${message}`);
            console.log("Errow while fetching videos list.");
        } else {
            throw new Error(e.message);
        }
    }
}



// Navigates between the videos per page and adds them (Maximum 50)
async function getPlaylistData() {
    try {
        const { videoIds, nextPageToken } = await getVideoIdsForPageToken();
        let pageToken = nextPageToken;
        newPageToken = pageToken;
        const returnedVideoIds = [];
        returnedVideoIds.push(getDetailsForVideoIds(videoIds));
        const videoGroups = await Promise.all(returnedVideoIds);



        for (const group of videoGroups) {
            for (const video of group) {
                finalTotalDuration += returnedToSeconds(video.contentDetails.duration);
            }
        }

        // console.log(videoIds);
        if (nextPageToken) {
            await getPlaylistData();
        }
    } catch (e) {
        throw new Error(e.message);
        console.log("Error while navigating between video pages.");
    }
}```

Solution

  • Assumptions

    1. finalTotalDuration is also a global variable declared somewhere (Not a good idea)
    2. You call getPlaylistData for multiple playlist for multiple users

    Solution

    You need to ensure the getPlaylistData is standalone and returns the finalTotalDuration as a return value (not set a global one)

    To make it standalone it has to be iterative in nature. It should be a recursive function which does the following

    async function getPlaylistTotalDuration(newPageToken) {
    
        // Step 1: Create the required query URL based on the newPageToken parameter
        // Step 2: Start a local duration counter
        // Step 3: Get the video details based on the URL created in Step 1
        // Step 4: Get the durations in seconds and add it to the local duration counter created in Step 2
        // Step 5: Check if the return of Step 3 has a nextPageToken, if so do a recursive call to self with the new token
        // Step 6: Return the final value, which will propogate back in a recursive function
    
    }
    

    You can simply call the function like

    let finalTotalDuration = getPlaylistTotalDuration(null); // or getPlaylistTotalDuration();
    

    for example the below getPlaylistTotalDuration is a replacement to your getPlaylistData method

    
    async function getVideoIdsForPageToken(url) {
        try {
            const { data } = await axios.get(url);
            const nextPageToken = data.nextPageToken;
            const videoIds = data.items.map((video) => {
                return video.contentDetails.videoId;
            });
            return { videoIds, nextPageToken };
        } catch (e) {
            if (e.response) {
                const { code, message } = e.response.data.error;
                throw new Error(`StatusCode ${code}. Reason: ${message}`);
                console.log("Errow while fetching videos list.");
            } else {
                throw new Error(e.message);
            }
        }
    }
    
    
    async function getPlaylistTotalDuration(newPageToken) {
        try {        
            // Step 1: Create the required query URL based on the newPageToken parameter
            let url = newPageToken
            ? `${playlistItemsURL}&playlistId=${extractedPlaylistIDId}&pageToken=${newPageToken}&key=${API_KEY}`
            : `${playlistItemsURL}&playlistId=${extractedPlaylistIDId}&key=${API_KEY}`;
    
            // Step 2: Start a local duration counter
            let totalDuration = 0;
    
            // Step 3: Get the video details based on the URL created in Step 1
            const { videoIds, nextPageToken } = await getVideoIdsForPageToken(url);
            const returnedVideoIds = [];
            returnedVideoIds.push(getDetailsForVideoIds(videoIds));
            const videoGroups = await Promise.all(returnedVideoIds);
    
            for (const group of videoGroups) {
                for (const video of group) {
                    // Step 4: Get the durations in seconds and add it to the local duration counter created in Step 2
                    totalDuration += returnedToSeconds(video.contentDetails.duration);
                }
            }
    
            // Step 5: Check if the return of Step 3 has a nextPageToken, if so do a recursive call to self with the new token
            if (nextPageToken) {
                totalDuration += await getPlaylistTotalDuration(nextPageToken);
            }
    
            // Step 6: Return the final value, which will propogate back in a recursive function
            return totalDuration;
        } catch (e) {
            throw new Error(e.message);
            console.log("Error while navigating between video pages.");
        }
    }
    

    Note: I have not actually ran the above code, but hopefully you get an idea of what needs to be done.