Search code examples
javascriptecmascript-6async-awaitaxioses6-promise

What type of Object needs to be returned into await for it not being undefined?


Let me be as clear as possible here.

  1. I call the getVids function
  2. It goes straight to its return await Promise.all...
  3. Then it goes into recursion where singleChannelWithNewVids object is being dumped with videos array
  4. When there is no more res.data.nextPageToken in response I'm returning that somewhat large singleChannelWithNewVids object.
  5. My console.log(singleChannelWithNewVids, 'RETURING') line logs that this object exists
  6. But when const gettingVids = await getVidsWithPage(e) happens and i log console.log('gettingVids', gettingVids) it logs that this object is undefined.

Why is that? I've tried everything as you can see commented and everyting returns undefined but logs a normal object...

const channelsWithPlaylistIds = [{
    channelName: 'TokyoStreetView - Japan The Beautiful',
    channelId: 'UCAxMEpfzdJ2dkrplSWehgcA',
    playlistIds: [ 'UUAxMEpfzdJ2dkrplSWehgcA' ],
    videos: []
}]

async getVids(channelsWithPlaylistIds) {
    const getVidsWithPage = async (e, pageToken) => {
        return axios(this.playlistItemsUrl(e.playlistIds[0], pageToken)).then(async res => {
            const singleChannelWithNewVids = { ...e, videos: [...e.videos, ...res.data.items]}

            if(res.data.nextPageToken) {
                console.log('RECUSING')
                await getVidsWithPage(singleChannelWithNewVids, res.data.nextPageToken)
            } else {
                console.log(singleChannelWithNewVids, 'RETURING')
                return Promise.resolve(singleChannelWithNewVids)
                // return new Promise(resolve => { resolve(singleChannelWithNewVids) })
                // return Promise.all([singleChannelWithNewVids])
                // return singleChannelWithNewVids
            }
        })
    }

    return await Promise.all(channelsWithPlaylistIds.map(async e => {
        const gettingVids = await getVidsWithPage(e)
        console.log('gettingVids', gettingVids)
        return gettingVids
    }))
}

yes i don't use semicolon, it's 2020, deal with it


Solution

  • What you have is a recursive function that does not return in all cases except the last.

    To understand this, imagine the thread of execution for the first execution of getVidsWithPage. It makes an axios call, sees that if(res.data.nextPageToken) is true, enters that if-statement, and then calls a function (itself in this case, but this is not actually important) and then ends execution without returning anything.

    During the recursion only the very last call to getVidsWithPage returns, but it returns to one of these:

    await getVidsWithPage(singleChannelWithNewVids, res.data.nextPageToken)
    

    ...and the result is discarded.

    Anyway, just add a return there:

    return await getVidsWithPage(singleChannelWithNewVids, res.data.nextPageToken)
    

    By the way, after seeing your comments, I'd like to suggest an unrelated improvement - YSK that every function marked async automatically, always returns a Promise. This is useful it means your Promise.resolve is redundant and you don't need to use .then() ever. You can rewrite your current code to be much more concise, like this:

    getVids(channelsWithPlaylistIds) {
        const getVidsWithPage = async (e, pageToken) => {
            let res = await axios(this.playlistItemsUrl(e.playlistIds[0], pageToken))
            const singleChannelWithNewVids = { ...e, videos: [...e.videos, ...res.data.items]}
    
            if(res.data.nextPageToken) {
                console.log('RECUSING')
                return getVidsWithPage(singleChannelWithNewVids, res.data.nextPageToken)
            } else {
                console.log(singleChannelWithNewVids, 'RETURING')
                return singleChannelWithNewVids
            }
        }
    
        return Promise.all(channelsWithPlaylistIds.map(getVidsWithPage))
    }