Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cache result from async function, and pass it to next function in Async.js

I've been trying to accomplish to following for the past couple of days, and I just can't solve it. I feel like I've tried everything. So here goes...

My route is given a JSON object which contains all information to create a new Quiz. In other words - an object which contains information about the new quiz, an array of questions, where each question contains an array of answers.

I use the async.js waterfall function, and want to do the following:

  • Save new quiz to DB, and pass the new quiz ID returned from the DB to the next waterfall function

  • Loop through each question. Cache the new question ID's returned from DB, and pass them to the next waterfall function. The problem here is caching the ID's. Since the function is async, I can't cache the result anywhere to be used in the next function...

  • Loop through each answer, and save them to DB

This is what I've got:

router.post('/quiz/create', function (req, res) {
    // JSON object with the new Quiz
    var json = req.body;
    async.waterfall([
        function(callback) {
            // Returns a Quiz ID for the new quiz, and passes it to the next waterfall function
            db.createQuiz(json, function(err, result) {
                // ID returned from DB is formatted as [{'': id}], hence result[0]['']
                callback(null, result[0]['']);
            });
        },
        function(quizID, callback) {
            // Loop through each question in the quiz
            async.forEachSeries(json.questions, function(question, callback) {
               // Save the question to DB, and get the new question ID returned
               db.createQuestion(question, quizID, function(err, result) {
                   // TODO: cache the new question ID's in an array somewhere to be passed to the next waterfall function
                   // Start next iteration of the loop
                   callback();
               });
            }, function(err) {
                // Done with all questions. Pass question ID's to next function
                callback(null, cachedQuestionIDs);
            });
        },
        function(cachedQuestionIDs, callback) {
            // TODO: access the question ID's, and use them to loop through and save answers to DB
        }
    ], function(err, result) {
        res.json({
           success: true,
           message: 'Quiz created!'
       });
    });
});
like image 919
Petter Avatar asked Sep 18 '25 03:09

Petter


2 Answers

Just store the values in an array outside the async.forEachSeries() but within the wrapping second function in the async.waterfall() control flow. You can then return this array when your async.forEachSeries() is done executing.

function(quizID, callback) {
  var cachedQuestionIDs = [];

  // Loop through each question in the quiz
  async.forEachSeries(json.questions, function(question, callback) {
    // Save the question to DB, and get the new question ID returned
    db.createQuestion(question, quizID, function(err, result) {
      // Store our result
      cachedQuestionIDs.push(result);

      // Start next iteration of the loop
      return callback();
    });
  }, function(err) {
    // Done with all questions. Pass question ID's to next function
    return callback(null, cachedQuestionIDs);
  });
}
like image 63
peteb Avatar answered Sep 19 '25 17:09

peteb


I finally got it to work correctly, with a more elegant solution using nested forEachSeries-loops for the questions and answers. No error handling here. Just to show the basic idea. This is the second function in the waterfall.

function(quizID, callback) {

        async.forEachSeries(json.questions, function(question, callback) {
            db.registerQuestion(question, quizID, function(err, result) {
                async.forEachSeries(question.answers, function(answer, callback) {
                   db.registerAnswer(answer, result[0][''], callback); 
                }, function() {
                    callback();
                });
            });
        }, function() {
            callback();
        });

}
like image 23
Petter Avatar answered Sep 19 '25 18:09

Petter