0
router.get('/pictures', (req, res, next) => {
  Picture.find()
  .then(handle404)
  .then(pictures => {
    return pictures.map(picture => picture.toObject())
  })
  .then(pictures => {
    pictures.map(picture => {
      User.findById(picture.owner)
      .then(owner => {
        picture.ownerName = owner.username
        console.log(pictures, "my picture with owner")
        return pictures
      })
      .then(pictures => res.status(200).json({ pictures: pictures }))
    })
  })
  .catch(next)
})
})

In order of operations: I need to find all pictures, then loop over the pictures array and find the username of the owner, then add a key in the pictures array with the owner username, then send the response with the new array of pictures that includes the owner's username

I want to return the pictures array with the found owner names.. but I am having an issue with the response being sent before the owner name is being set and Im not sure how to get the response to wait. If there is just one owner name its fine, but more than one and I get an error -

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I think this means my response is being sent before my query is completed.

Colleen
  • 48
  • 7
  • 1
    My picture is an object with multiple key value pairs, When I say add a key in the pictures array - i mean add the key to each picture object in the array – Colleen Mar 26 '21 at 00:57
  • You're sending the response *inside* the `map` loop, once for each picture. Collect an array of promises, use `Promise.all`. – Bergi Mar 26 '21 at 01:06

1 Answers1

1

When looping with an asynchronous operation in the loop, you have to either sequence the loop with await or, for parallel operation, collect all the promises from the loop into an array and use Promise.all() to track when they are all done. Here's one way to do that:

router.get('/pictures', (req, res, next) => {
    Picture.find()
        .then(handle404)
        .then(pictures => {
            pictures = pictures.map(picture => picture.toObject());
            return Promise.all(pictures.map(picture => {
                return User.findById(picture.owner).then(owner => {
                    picture.ownerName = owner.username
                    return picture;
                });
            }));
        }).then(pictures => {
            res.status(200).json({ pictures });
        }).catch(next);
});

Also, when you have asynchronous operations inside .then() handlers, make sure to return those promises so they are linked into the chain.


The warning you are getting UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client is because you are trying to send more than one response to the same http request. That is happening inside your loop where you're trying to do res.json(...) inside the loop (and thus multiple times). The way to avoid that is to collect the results from the loop (using Promise.all() in this case) and then send one response at the end.

jfriend00
  • 683,504
  • 96
  • 985
  • 979