Reputation: 1996
I have 3 functions which I want to run asynchronously and when they are all done run another function:
app.get('/', function (req, res) {
var homePosts = {
newsest: [],
reviewed: [],
mostPopuler: [],
viewed: []
};
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages() {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.newsest = posts;
}
}).limit(4).sort( { date : -1 } );
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages() {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.reviewed = posts;
}
}).limit(4).sort( { commentsNumber : -1 } );
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages() {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.mostPopuler = posts;
}
}).limit(4).sort( { likesNumber : -1 } );
}
// now run all 3 functions and when they are done render home page with the homePosts object which contains proper pages
async.parallel([
fetchNewestPages,
fetchMostReviewedPages,
fetchMostPopularPages
], function (err) { // it doesn't run at all
if (err) throw err;
console.log(homePosts);
res.render("home", {homePosts}); // render home page with the proper pages
});
});
hope that you got what the code does, here is the description of what the code does:
The last function which will render home page doesn't run at all. Where is my problem? is there any way to run those 3 functions simultaneously and then run the last function which will render a page?
UPDATE:
I've managed to do that in this way but they are not running simultaneously, they are running one after another.
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages(cb) {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.newsest = posts;
cb();
}
}).limit(4).sort( { date : -1 } );
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages(cb) {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.reviewed = posts;
cb();
}
}).limit(4).sort( { commentsNumber : -1 } );
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages(cb) {
Post.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage", function (err, posts) {
if (err) {
req.flash('error', 'An unknown error was occured.');
res.redirect('back');
} else {
homePosts.mostPopuler = posts;
cb();
}
}).limit(4).sort( { likesNumber : -1 } );
}
fetchNewestPages(function () {
fetchMostReviewedPages(function () {
fetchMostPopularPages(function () {
res.render("home", {homePosts});
});
});
});
Upvotes: 0
Views: 89
Reputation: 1702
Your problem is you are not having a callback parameter in any of your functions. Remember, you have to call the callback method when one function's processing is completed.
What I do in my practice is use async.constant
as the first method of async.waterfall
or async.parallel
and pass the data that is to be used in the async methods. In your case it can be the search criterion for all three methods.
If no data is to be used in async methods, then I just pass an empty JS object.
Using async.constant
helps me in two things.
In your case, the async.constant
method will have homePosts
object.
app.get('/', function (req, res) {
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages(data, callback) {
Post
.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage")
.limit(4)
.sort( { date : -1 } )
.exec(function (err, posts) {
if (err) {
//If we pass first parameter as non-null, the control is passed to the last optional callback skipping all other functions in case of async.waterfall and not waiting for other functions to complete in case of async.parallel
return callback('An unknown error was occured.');
} else {
data['newsest'] = posts; //since homePosts is data object inside this function
If this function is completed successfully then we pass first parameter as null (no error) and second parameter as our object. As the strategy is parallel, all three functions will be editing the same object 'homePosts'
return callback(null, data);
}
});
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages(data, callback) {
Post
.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage")
.limit(4)
.sort( { commentsNumber : -1 } )
.exec(function (err, posts) {
if (err) {
//read comment in first function
return callback('An unknown error was occured.');
} else {
data['reviewed'] = posts; //since homePosts is data object inside this function
//read comment in first function
return callback(null, data);
}
});
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages(data, callback) {
Post
.find({ "type": "public", "featuredImage": { "$exists": true } },"_id title briefDes featuredImage")
.limit(4)
.sort( { likesNumber : -1 } )
.exec(function (err, posts) {
if (err) {
//read comment in first function
return callback('An unknown error was occured.');
} else {
data['reviewed'] = posts; //since homePosts is data object inside this function
//read comment in first function
return callback(null, data);
}
});
}
var homePosts = {
newsest: [],
reviewed: [],
mostPopuler: [],
viewed: []
};
// now run all 3 functions and when they are done render home page with the homePosts object which contains proper pages
async.parallel([
async.constant(homePosts),
fetchNewestPages,
fetchMostReviewedPages,
fetchMostPopularPages
], function (err, data) {
//once all functions complete their execution and their callback method is called, with or without error, this method will be called.
if (err) {
req.flash('error', err);
res.redirect('back');
} else {
console.log(data);
res.render("home", {data}); // render home page with the proper pages
}
});
});
Hope that solves your problem and clear your concept a bit more.
Upvotes: 2
Reputation: 22553
The async library works with functions that use callbacks. None of yours do.
Either rewrite them in callback form, or use something like Promise.all:
Promise.all(
[fetchNewestPages,
fetchMostReviewedPages,
fetchMostPopularPages])
.then(res => console.log(res[0], res[1], res[2]))
.catch(err => console.log(err))
Upvotes: 1
Reputation: 390
Hope this helped you
console.log('start');
// fetch newest pages and asign the result to 'homePosts.newest '
function fetchNewestPages() {
console.log('1')
}
// fetch most reviewed pages and asign the result to 'homePosts.reviewd '
function fetchMostReviewedPages() {
console.log('2')
}
// fetch most popular pages and asign the result to 'homePosts.mostPopuler '
function fetchMostPopularPages() {
console.log('3')
}
fetchNewestPages();
console.log('1 DONE');
fetchMostReviewedPages();
console.log('2 DONE');
fetchMostPopularPages();
console.log('3 DONE');
I work also alot with interval. As example If I had alot of callbacks and something is suddenly not synchron then this trick can be nice
var obj = {}
// to somelong stuff here and the result is var result
var result = 'this was generated in the fictive long process above'
objectme.obj = result // those object string can be used anywhere in the script realy nice.
clearInterval(testinterval); // <-- do also here a clearinterval
var testinterval = setInterval(function(){
if (objectme.obj) {
clearInterval(testinterval);
//.. the interval only stop here at the if. you can do anything here. also you can make a timeout. This will force the script to run as you wish it
}
},10000);
REALLY IMPORTANT. If you plan to insert long code into clearinterval zone then you need to increase the interval time. If your inserted codes takes longer than the interval, then your code will be execuded 2 times.
However you should do like in the first example. because using interval can be very tricky.
Upvotes: 0