1
votes

I am trying to make a bunch of nested calls to the database and want to do something once the results have all come back. I'm new to promises so forgive me if I'm totally doing this wrong (probably the case)

My code currently is something like this:

getVideos(user).then((videos) => {
  videos.forEach((video) => {
    getImage(video).then(() => {
      getMembers().then((members) => {
        getComments().then((comments) => {
          getKeywords().then((keywords) => {
            getTranscript().then((transcript) => {
              console.log(members, comments, keywords, transcript)
            }
          }
        }
      }
    })
  })
})

This is obviously super inefficient because getMembers(), getComments(), getKeywords(), and getTranscript() dont need to wait for each other and can all be called asynchronously. Also getImage() can be called asynchronously for every video in the loop without waiting.

I'm trying to change the code to bundle up these promises into one big one so once it resolves I can access all the fetched data in one place. This is my attempt but it prints

Promise{...}, Promise{...}, Promise{...}, Promise{...}

instead of the fetched data.

Any idea what I'm doing wrong and how I can properly change all these nested promises into one? Thanks

let members, comments, keywords, transcript

getVideos(user).then((videos) => {
   let promises = []
   videos.forEach((video) => {
      let p = getImage(video).then(() => {
        members = getMembers()
        comments = getComments()
        keywords = getKeywords()
        transcript = getTranscript()
        return Promise.all([members, comments, keywords, transcript])
      })
      promises.push(p)
  })
  return Promise.all(promises)
})
.then(() => {
  console.log(members, comments, keywords, transcript)
})
1
What if you console.log(arguments); instead? - ugh StackExchange
You'll need to set global state with the data the embedded promises ( members, comments...) resolve with and print contingent on this state or move the output routines into .then handlers for said promises. - collapsar
The end-solution will definitely depend on what you need, but I'd recommend taking a look at Promise.all. It allows you to wait for multiple promises before continuing execution. So if you want to concurrently retrieve all of the information about a particular video, you can. If you want to wait for all of the video information to have returned, you can do that do. - Hodrobond
It's not clear what depends on what here. getMembers(), getComments(), getTranscript(), etc. don't take any arguments. So do they always return the same thing? Do they depend on anything happening before calling them? Are they reading a global variable? - Mark
If you want to serialize your requests, then chain your promises. Return the inner promises. Search for "promise chaining" to get more info. But, you probably don't need to serialize them. You can issue all the requests and use Promise.all() to know when they are all done. - jfriend00

1 Answers

6
votes

Try using two Promise.alls instead - one for each video, and one for the members, comments, keywords, and transcript:

getVideos(user).then((videos) => Promise.all(
  videos.map(video => getImage(video)
    .then(videoResp => Promise.all([
      getMembers(), // do you need to call these functions with `videoResp`?
      getComments(),
      getKeywords(),
      getTranscript(),
    ]))
  )
));

The promise chain will then resolve with something like

[ [
    // video 1
    v1members,
    v1comments,
    v1keywords,
    v1transcript,
  ],
  [
    // video 2
    v2members,
    v2comments,
    v2keywords,
    v2transcript,
  ],
  // ...
]