最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to wait for a loop to finish running before sending back a response in NodeJs? - Stack Overflow

programmeradmin1浏览0评论

I'm trying to send back an array of objects after a GET request to a URL but I can't figure out how to structure the logic so the return array fully populates before sending it to the client.

Below is my server side code for responding to the request. I used passport.js previously in the code to create a login portal and user object. I'm trying to take the "connections" array from the user who is making the request and sending them back an array of objects with the connection's name and picture. I know that the code below is incorrect syntax wise but it's an overview of what I'm trying to acplish. I've tried doing it the callback way but that just kept leading me in circles because I couldn't figure out the correct logic for it.

router.get('/data', function(req, res, next) {
    var IdArr = req.user.connections;

    var retArr = [];

    function getUsers() {
        for (i = 0; i < IdArr.length; i++) {
            User.getUserById(IdArr[i], function(err, patient) {
                retArr[i] = {name:patient.name, pic:patient.profileImage};
            });
        }
    }

    function getDataAndSend() {
        function(getUsers(), function sendRes() { // I know this is incorrect syntax
            console.log(retArr);
            res.json(retArr);
        });
    }

    getDataAndSend();
});

I'm trying to send back an array of objects after a GET request to a URL but I can't figure out how to structure the logic so the return array fully populates before sending it to the client.

Below is my server side code for responding to the request. I used passport.js previously in the code to create a login portal and user object. I'm trying to take the "connections" array from the user who is making the request and sending them back an array of objects with the connection's name and picture. I know that the code below is incorrect syntax wise but it's an overview of what I'm trying to acplish. I've tried doing it the callback way but that just kept leading me in circles because I couldn't figure out the correct logic for it.

router.get('/data', function(req, res, next) {
    var IdArr = req.user.connections;

    var retArr = [];

    function getUsers() {
        for (i = 0; i < IdArr.length; i++) {
            User.getUserById(IdArr[i], function(err, patient) {
                retArr[i] = {name:patient.name, pic:patient.profileImage};
            });
        }
    }

    function getDataAndSend() {
        function(getUsers(), function sendRes() { // I know this is incorrect syntax
            console.log(retArr);
            res.json(retArr);
        });
    }

    getDataAndSend();
});
Share Improve this question edited Dec 16, 2018 at 2:46 Kevin Pastor 8014 silver badges19 bronze badges asked Dec 15, 2018 at 22:33 Ryan O'SheaRyan O'Shea 671 silver badge5 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 5

First of all, you should declare i, and when you do it should be with block-scope (let), so that the nested callback function will use the same variable.

You could check how many entries in retArr have been retrieved and call res.json once you know you have them all.

router.get('/data', function(req, res, next) {
    var IdArr = req.user.connections;
    var retArr = [];
    for (let i = 0; i < IdArr.length; i++) {
        User.getUserById(IdArr[i], function(err, patient) {
            retArr[i] = {name:patient.name, pic:patient.profileImage};
            if (Object.keys(retArr).length === IdArr.length) res.json(retArr);
        });
    }
});

I think the easiest way to deal with these things is with Promises. Any async function written in the callback way (e.g. this User.getUserById) can be turned into a function that returns a promise. You just wrap the call around a new Promise and resolve when you're done.

Say in your case.

function promiseGetById(id) {
    return new Promise((resolve, reject) =>
        User.getUserById(id, (err, pat) => resolve(pat))
    );
}

Then something like

Promise.all(IdArr.map(id => promiseGetById(id))).then(arr =>
    res.json(
        arr.map(patient => ({ name: patient.name, pic: patient.profileImage }))
    )
);

Alternatively if you don't like promises you can do it by having a counter or something which in each callback you increment and then in your callback you res.json when the counter is the correct value.

Change all your function logic to return promises and use async/await for code clarity.

const getUserById = (id) => {
  return new Promise((resolve, reject) => {
    User.getUserById(IdArr[i], function(err, patient) {
      resolve({name:patient.name, pic:patient.profileImage});
    });
  });
}

const getAllUsers = async(idArr) => {
  const retArr = [];

  // for...of loop to await without continuing the loop
  // but this will execute only sequentially
  for (let id of idArr) {
    const ret = await getUserById(id);
    retArr.push(ret);
  }

  // for parallel execution, use Promise.all()
  await Promise.all([...idArr.map(id => getUserById(id))]);

  return retArr;
}

router.get('/data', async (req, res, next) => {
  var IdArr = req.user.connections;
  var retArr = await getAllUsers(IdArr);

  console.log(retArr);
  res.json(retArr);
});

You seem to be trying to extract way to much functions for an algorithm that isn't that plicated. The code below initialy extract the information it needs for the request. Then, it populates the array needed for the response and simply sends it.

router.get('/data', (req, res, next) => {
    const idArray = req.user.connections;

    let responseArray = [];

    // loop to populate responseArray
    for (let id of idArray) {
        User.getUserById(id, (err, patient) => {
            // add to the end of the array the patient informations
            responseArray.push({
                name: patient.name,
                pic: patient.profileImage
            });
        });
    }

    // send back responseArray
    res.json(responseArray);
});
发布评论

评论列表(0)

  1. 暂无评论