Code Snippet - How to do Async Programming in Node.js
Table of Contents
- Introduction
- Simple Example of Using Async
- Another Example of Using Async with Mongoose and Node.js
- Conclusion
Introduction
A short sample code to describe how to use the npm async module waterfall process. Since Node.js and Mongoose are asynchronous, if you want to process your logic sequentially — like update step 1, then do step 2, and more — using the async module is one way.
Simple Example of Using Async
Example code to use async waterfall
async.waterfall(
[
function (callback) {
console.info("1");
callback(null, 'one', 'two');
},
function (arg1, arg2, callback) {
console.info("2");
console.info(arg1 + arg2);
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function (arg1, callback) {
console.info("3");
console.info(arg1);
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
console.info("4");
console.info(err);
console.info(result);
});
The result shown by the above logic:
1
2
onetwo
3
three
4
null
done
Another Example of Using Async with Mongoose and Node.js
First, I had logic code written in C# with the C# MongoDB driver. Following is the sample code:
private object GetTagsItems()
{
var tags = DomainContext.DEALS.All().DistinctBy(x => x.TagId).Take(20);
List tagItems = new List();
foreach (var tag in tags)
{
List tagDeal = new List();
var deals = DomainContext.DEALS.All(x => x.TagIds.Contains(tag.Id)).Take(4);
foreach (var deal in deals)
{
tagDeal.Add(new
{
Title = deal.Title,
SlugTitle = deal.SlugTitle,
ImageUrl = deal.PrimaryImageUrl,
});
}
tagItems.Add(new
{
TagName = tag.Name,
TagId = tag.TagId,
Items = tagDeal
});
}
return tagItems;
}
I was trying to move the code to Node.js with Mongoose. First, I wrote something like the following in Node.js. It didn't work — at the UI, data was always null. Then I noticed that JavaScript is non-blocking and asynchronous, not like C#. So I tried to use the Node.js async module.
var tagQ = tagLabel.find({}).limit(20);
tagQ.exec(function (err, tags) {
var result = {};
var tagItems = [];
tags.forEach(function (tag) {
var dealQ = dealLineItem.find({}).where('DealId'). in ([1]).limit(4);
dealQ.exec(function (err, dealItems) {
if (!err) {
console.info(err);
}
var tagDeals = [];
dealItems.forEach(function (dealItem) {
var deal = {};
deal.Title = dealItem.Title;
deal.SlugTitle = dealItem.SlugTitle;
deal.ImageUrl = dealItem.PrimaryImageUrl;
tagDeals.push(deal);
})
var tagItem = [];
tagItem.TagName = tag.Name;
tagItem.TagId = tag.TagId;
tagItem.Items = tagDeals;
tagItems.push(tagItem);
result.name = 'test';
result.items = tagItems;
locals.data = result;
return res.json(result);
});
});
res.render('index.jade', { data: locals.data });
});
Following is another example which works. In this example, after everything finishes, the code goes to async.parallel to do the task.
var items = [];
var myCalls = [];
tagIds.forEach(function (tagId) {
myCalls.push(function (callback) {
tagLabel.findOne({ TagId: tagId }, function (err, tag) {
var dealQ = dealLineItem.find({ TagIds: mongoose.Types.ObjectId(tag._id) }).limit(4);
dealQ.exec(function (err, dealItems) {
var deals = [];
dealItems.forEach(function (dealItem) {
var deal = {};
deal.Title = dealItem.Title;
deals.push(deal);
});
var tagItem = {};
tagItem.TagName = tag.Name;
items.push(tagItem);
callback(null, null);
});
});
});
async.parallel(myCalls, function (err, result) {
if (err)
return console.log(err);
locals.data =
{
Tags: items
};
res.render('index.jade', { data: locals.data });
});
});
Conclusion
The async module in Node.js is useful for handling sequential or parallel asynchronous operations. Using async.waterfall allows you to chain steps together, while async.parallel lets you run tasks concurrently and collect results when all are done. This is especially helpful when migrating synchronous logic from languages like C# to the asynchronous Node.js environment.