The comments helper

Let's take a look at the first use of async in one of our helpers, the comments helper. Originally, helpers/comments.js was a module that had a newest function that returned an array of fixture data with some sample comments.
We are going to completely remove this code and instead query MongoDB for the newest comments and return those as an array. Start by clearing the comment helper module and starting from scratch (note that we included a new callback parameter to the newest function):

var models = require('../models'), 
async = require('async'); 
module.exports = { 
newest: (callback)=>{ 
        // to do... 
    } 
}; 

Notice that we added the additional require statements at the top of the file for our models and async. Within the newest function, let's replace the // to do... comment with code to query MongoDB and find the five most recent comments:

models.Comment.find({}, {}, {
limit: 5,
sort: { 'timestamp': -1 }
},
(err, comments) => {
// to do - attach an image to each comment...
});

Note that the first parameter in the find query is an empty JavaScript object, meaning we will retrieve every comment in the database. For the third parameter, however, we're using limit and sort so that we limit the number of records returned to five, and we sort the query by timestamp in descending order.

Now that we have an array of comments, we'd ideally like for the image that each comment belongs to to be returned as well. Typically, this would be accomplished by using an aggregate query in MongoDB to join different collections together (such as a JOIN in SQL). We will see aggregate in more detail in the next chapter.
For the purposes of our code, we're going to instead query MongoDB separately for each comment and retrieve the image associated with the comment's image_id value.

First, let's define a function that will query MongoDB and retrieve and attach an image model to a comment model:

var attachImage = (comment, next) => {
models.Image.findOne({ _id: comment.image_id },
(err, image) => {
if (err) throw err;
comment.image = image;
next(err);
});
};

This function will accept a comment model as the first parameter and a callback function (named next) as the second parameter . Having the next callback as the second parameter is important, because it's the key to how async is able to function. Imagine that the next callback acts as a chain link. Since the same function is going to be called for every item in a collection, there needs to be a way to daisy-chain the calls together. This is performed via the callback.

Basically, every time the callback is called for an item in the array, it performs its work and then executes the same callback with the next item in the array, and so on and so forth, which is why we named the callback function parameter next.

Another important element to point out with this function is that when we attach the image model to the comment's image property, we are using the virtual property we set up earlier in the main comment's schema. If you recall, when we set the image property, we were actually setting the private _image property. Likewise, when we get the image property, we are actually retrieving the private _image property.

After we define the attachImage function, we need to use the each function of async to apply that function to every item in the comments collection:

async.each(comments, attachImage,
(err) => {
if (err) throw err;
callback(err, comments);
});

The each function of async will loop through every item in the collection in the first parameter, and send each item as a parameter to a callback function in the second parameter. The third parameter is the final callback function that is executed once the entire series is finished with the collection. In this case, every comment in the comment's array will be passed individually to the attachImage function. When the entire collection has been iterated through, the final callback will execute, which basically fires the very first callback function that was passed into the newest function as its only parameter. Boy, that was a mouthful! Let's try to break this down a little further, so it makes a bit more sense:

  • The newest function of the comment helper module accepts a single parameter named callback- -this is the function that will be called
    once all of the work in this entire function is finished.
  • The first thing the newest function does is find the latest five comments
    and return them as an array to an anonymously defined inline function.
  • First, we define a function and store it in a variable named attachImage.
  • The attachImage function accepts two parameters: an individual comment model, and a callback function that we named next.
  • The attachImage function will query MongoDB to find an image with
    an _id value that is the same as the image_id property of the comment
    that was passed into it as the first parameter.
  • Once that image is found, it is attached to the comment via its image property and then the next callback function is executed.
  • We use async.each to loop through every comment in the comments array that was passed as the first parameter to each.
  • Pass the attachImage function as the second parameter, which is the function that will be called for every comment in the comment's array.
  • Finally, define an inline anonymous function that will be executed once the last item in the comments collection has been iterated on. This inline function itself only accepts an error object as its parameter. Assuming every iteration of the comments collection was successful, this function will be executed with no error. Inside this function, we execute the original function named callback that was the only parameter to the newest function, and callback is called with the newly updated comment's array as its second parameter.

Okay, the hardest part is over! You survived a crash course on the async module and came out, hopefully unscathed! Just to be safe, here is the code for the helpers/comments.js module file in its entirety:

/* jshint node: true */ 
"use strict" 
 
var models = require('../models'), 
    async = require('async'); 
 
module.exports = { 
    newest: (callback)=>{ 
        models.Comment.find({}, {}, { limit: 5, sort: { 'timestamp': -1 } }, 
            (err, comments)=>{ 
                //console.log("COCOCO"); 
                //console.log(comments); 
                var attachImage = (comment, next)=>{ 
                    models.Image.findOne({ _id : comment.image_id}, 
                        (err, image)=>{ 
                            if (err) throw err; 
 
                            comment.image = image; 
                            next(err); 
                        }); 
                }; 
 
                async.each(comments, attachImage, 
                    (err)=>{ 
                        if (err) throw err; 
                        callback(err, comments); 
                    }); 
            }); 
    } 
};
Callbacks, callbacks, callbacks everywhere!
At this point, it's probably getting a little confusing with the number of callbacks we've been dealing with. A part of the problem is the terminology we've been using. Any function that is passed as a parameter and only executed after certain conditions are met, typically as the end result of the original function, is referred to as a callback. The popular convention with JavaScript is to label a callback function in a parameter literally with the variable name callback, so that it's obvious. This works great when you are reading code, but not so much when you are explaining code and referring to a function named callback that's also known as the callback!
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.21.152