Search code examples
node.jspromisereturnfetchchain

Chaining fetch, fetch, map, filter, fetch, fetch


How my problem is different: After I successfully chain promises I'm left with separate arrays listing files, and directories, need to fetch again the files in those directories, add it to the first list of files, then finally fetch based on all of the completed list of files.

Each function on their own, including another not shown to descend into directories and list the files there as well, works.

At the end of the day I just need the contents of any package.json files owned by me in all of my github repos, if I'm going at this the long/wrong way.

I did try to return an object

{
files: files,
paths: paths
}

but the object was empty every time.

listRepos = async () => {
    const path = '/user/repos'
    return fetch(api + path, options)
    .then(res => res.json())
    .then(json => json.filter(repo => {
        return repo.owner.login === user
    }))
}

listContents = async (repo) => {
    const path = `/repos/${repo.owner.login}/${repo.name}/contents`
    return fetch(api + path, options)
    .then(res => res.json())
}

getNext = async () => {
    let contents = []
    let repoList = await listRepos()
    return repoList.map(async (repo) => {
        await listContents(repo).then(async (contents) => { // This happens for each repo
            let pathList = await contents.filter(entry => {
                return entry.type === 'dir' && entry.name != 'node_modules'
            })
            pathList.forEach(path => paths.push(path))
            let fileList = await contents.data.filter(entry => {
                return entry.name === 'package.json'
            })
        })
        return {
            files: fileList,
            paths: pathList
        }
    })
}

I'm back and forth between either lot's of

Promise { <pending> }

or "Unhandled Promise Rejection"'s

I do realize I need to catch all of my errors, I cut those out of the code here for space and readability.


Solution

  • You're getting a lot of Promise { <pending> } because you're mapping your repoList to promises :

    return repoList.map(async (repo) => {
    

    Async functions always return a promise. You want the values those promises resolve to. You can get the values of an array of promises using Promise.all:

    Promise.all([promise, promise]).then(values => console.log(values)
    // ['foo', 'bar']
    

    So you have a list of repos, then you map this list to promises, then you have to map that back to resolved values:

    const values = await Promise.all(['foo', 'bar'].map(val => fetchSomething(val)))
    

    In your case:

    return Promise.all(repoList.map(async (repo) => {
        ...
    }));
    

    Promise.all returns a promise, so remember to use await getNext().


    Inside your repoList mapping function, you're declaring let pathList and let fileList inside the listContents mapping function scope, and then trying to access them from the repoList mapping function scope. You probably want to return them directly, like this:

    getNext = async () => {
        let contents = []
        let repoList = await listRepos()
        return repoList.map((repo) => {
            return listContents(repo).then(async (contents) => { // This happens for each repo
                let pathList = await contents.filter(entry => {
                    return entry.type === 'dir' && entry.name != 'node_modules'
                })
                pathList.forEach(path => paths.push(path))
                let fileList = await contents.data.filter(entry => {
                    return entry.name === 'package.json'
                })
                return {
                    files: fileList,
                    paths: pathList
                }
            })
        })
    }