Vinay Kashyap
Vinay Kashyap

Reputation: 101

file write issue in Async/Await

Here I am trying to retrieve objects and push them into the array. For some reason there is only one record being pushed into the file when it should contain more objects. Can you help me out with this or let me know where I am going wrong? Here is my code:

exports.createjson = (req, res, next) => {
  try {
    var myPromise = () => {
      // ...
    };
    var callMyPromise = async () => {
      const responsearray = [];
      var result = await myPromise();
      return new Promise((resolve, reject) => {
        result.forEach(element => {
          NewsModel.findOne({ _id: element.newsId }).exec(
            async (err, result) => {
              if (err) {
                throw err;
              }
              reportsModel
                .findOne({
                  $and: [
                    { userId: req.query.userId },
                    { newsId: element.newsId }
                  ]
                })
                .exec((err, newsResult) => {
                  if (err) {
                    throw err;
                  }
                  // console.log(newsResult);

                  var response = {
                    newsId: element.newsId,
                    title: result.title,
                    collection: result.group,
                    belivibalityIndex: element.belivibalityIndex,
                    priorknowledge: element.priorknowledge,
                    readingTime: element.readingTime,
                    userId: element.userId,
                    comment: element.comment,
                    report: newsResult !== null ? newsResult.feedback : null
                  };
                  // #all object pushed and displayed in console
                  responsearray.push(response);
                  console.log(response);
                  console.log(responsearray.length);
                  // let data = JSON.stringify(responsearray);
                  // #here is the issue          // fs.writeFileSync("abc.json", data, null, null, flag = 'a');
                  return responsearray;
                });
            }
          );
        });
      });
    };
    callMyPromise().then(function(responsearray) {
      res.json(responsearray);
    });
  } catch (error) {
    next(error);
  }
};

Upvotes: 0

Views: 90

Answers (1)

Always Learning
Always Learning

Reputation: 5581

You're not quite using Promises properly. For example, you create a Promise object but never call the resolve/reject functions. In the forEach loop you are calling functions that use callbacks and when that work is done you can resolve the promise you're wrapping it in.

Also you're calling res.json and writing the file (though it's commented out) while you're in the forEach loop. That means res.json will get called multiple times, which is not allowed. You can only have one response from an http request.

I restructured the code so that it collects each promise in an array of Promises then waits for all of them to resolve. Only after all of the work is done, we can write the file and call res.json to complete the http request.

exports.createjson = async (req, res, next) => {
  const responsearray = [];
  var elements = await myPromise();
  var promises = []; // collect a bunch of promises to wait on
  elements.forEach(element => {
    // one promise per element that resolves when response is on the array
    var promise = new Promise(function(resolve, reject) {
      NewsModel.findOne({ _id: element.newsId }).exec((err, result) => {
        if (err) { return reject(err); }
        reportsModel
          .findOne({
            $and: [{ userId: req.query.userId }, { newsId: element.newsId }]
          })
          .exec((err, newsResult) => {
            if (err) { return reject(err); }

            var response = { /* response body */ };
            responsearray.push(response);
            console.log(response);
            console.log(responsearray.length);
            // complete the promise now that the response is on the array
            return resolve(); 
          });
      });
    });
    // collect each promise in an array so we can wait for them all 
    promises.push(promise);
  });
  // wait for all the work to complete
  await Promise.all(promises).catch(err => next(err));
  // write the responsearray to a file as json 
  let data = JSON.stringify(responsearray);
  fs.writeFileSync("abc.json", data);
  return res.json(responsearray);
};

I also removed the try/catch block since the Promise allows you to use .catch in a cleaner way. It simplifies the nesting which makes it easier to read.

The key takeaway here is the general structure:

// get your array to work with
var array = await someFunction()
var manyPromises = []
var manyResults = []
// for each thing in the array create a promise
array.forEach( thing => {
  manyPromises.push( new Promise((resolve,reject) => {
    doSomething(thing, (err, result) => {
      if (err) return reject(err);
      // store the results in the array and resolve the promise
      manyResults.push(result)
      return resolve();
    });
  });
});
// wait for all promises in manyPromises to complete
await Promise.all(manyPromises).catch(err => return next(err));
// now the many promises are done and manyResponses are ready
saveResponsesToFile(JSON.stringify(manyResponses))
return res.json(manyReponses)

Upvotes: 1

Related Questions