1

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)
})
response.write
  • 175
  • 5
  • 23
Egor Egorov
  • 705
  • 3
  • 10
  • 22
  • What if you `console.log(arguments);` instead? – Blue Jul 25 '18 at 23:56
  • 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 Jul 25 '18 at 23:59
  • The end-solution will definitely depend on what you need, but I'd recommend taking a look at [Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 Jul 26 '18 at 00:00
  • 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 Jul 26 '18 at 00:05
  • 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 Jul 26 '18 at 00:14
  • @OP Exactly which requests are dependant on which other requests being completed first?Your code seems to imply that `getMembers` and such are dependant on the `image`, but you're not calling any of the other `get*` functions with the image result (nor are you using the image result at all...? might you just omit `getImage` completely?) It's not entirely clear what essential parts of the code have been omitted for simplicity (if any) – CertainPerformance Jul 26 '18 at 00:32

1 Answers1

6

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,
  ],
  // ...
]
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • One might ask why `getImage()` can't be parallelized along with `getMembers()`, `getComments()` and so on all in the same `Promise.all()` since none of these seem to depend upon one another. – jfriend00 Jul 26 '18 at 00:28
  • Good question, should have clarified: If there is no image I abort the query. No image = no need to get all other stuff – Egor Egorov Jul 26 '18 at 01:31
  • @EgorEgorov - Does "no image" mean that `getImage()` rejects or does one have to check the return value when it resolves? – jfriend00 Jul 26 '18 at 02:49