Reputation: 6911
I have this code snippet:
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match;
while (match = re.exec(body)){
var href = match[1];
var title = match[2];
console.log(href);
db.news.findOne({ title: title }, function(err, result){
if (err) {
console.log(err);
} else {
console.log(href);
// more codes here
}
});
}
Here is the sample output:
news/2015/02/20/347332.html
news/2015/02/19/347307.html
news/2015/02/19/347176.html
news/2015/02/19/347176.html
news/2015/02/19/347176.html
news/2015/02/19/347176.html
So, I have three sets of data to be passed to findOne function. However, only the last one got passed in three times. How to workaround?
UPDATE based on jfriend00 and Neta Meta, these are the two ways to make it work:
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var cnt = 0;
function next(){
var match = re.exec(body);
if (match) {
var href = match[1];
var title = match[2];
db.news.findOne({ title: title }, function(err, result){
if (err) {
console.log(err);
} else {
console.log(href);
// more codes here
}
});
}
}
next();
Or
var asyncFunction = function(db, href, title){
db.news.findOne({ title: title }, function(err, result){
if (err) {
console.log(err);
} else {
console.log(href);
// more codes here
}
});
}
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match;
var cnt = 0;
while (match = re.exec(body)) {
asyncFunction(db, match[1], match[2]);
}
Upvotes: 1
Views: 167
Reputation: 708046
The reason you don't get the output you expect is because you're sharing the href
and title
variables for all your database calls. Thus, those aren't kept track of separately for each async database operation.
If you're OK with all your async functions executing at once and the data can be processed in any order, then you just need to create a closure so capture your local variables separately for each invocation of the loop:
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match, cntr = 0;
while (match = re.exec(body)){
(function(href, title, index) {
console.log(href);
db.news.findOne({ title: title }, function(err, result){
if (err) {
console.log(err);
} else {
console.log(href);
// more codes here
}
});
})(match[1], match[2], cntr++);
}
If you want to issue the requests serially (only one at a time), then you can't really use the while
loop to control things because it's going to launch them all at once. I tend to use this type of design pattern with a next()
local function instead of the while
loop for serial operation:
function someFunction() {
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
function next() {
var match = re.exec(body);
if (match) {
var href = match[1];
var title = match[2];
db.news.findOne({ title: title }, function(err, result){
if (err) {
console.log(err);
} else {
console.log(href);
// more codes here
// launch the next iteration
next();
}
});
}
}
// run the first iteration
next();
}
Using promises, you could promisify()
the db.news.findOne()
function so it returns a promise, collect all the matches into an array and then use .reduce()
to sequence all the database calls with the promise's .then()
method providing the sequencing.
Upvotes: 2
Reputation: 4047
The reason you only get the last href is because while iterates and call fineOne which is an asyc operation. while wont wait till the findOne finishes it just continue running by the time the findOne finishes while got to the end of the loop and that's why you're getting the same href.
there are several ways you could do that, 1 promises(prefered in my opinion) - you will have to read about promisses to learn more. however checkout: https://github.com/petkaantonov/bluebird http://www.html5rocks.com/en/tutorials/es6/promises/ and http://promise-nuggets.github.io/articles/03-power-of-then-sync-processing.html
Wrapping your async function in another function and binding whatever you want to it ( not a good option but possible)
// wrapping your async function.
var asyncFunction = function(title,href, successCb, failCb){
db.news.findOne({ title: title }, function(err, result){
if (err) {
failCb();
} else {
successCb()
}
});
};
var re = new RegExp("<a href=\"(news[^?|\"]+).*?>([^<]+)</a>", "g");
var match;
while (match = re.exec(body)){
var href = match[1];
var title = match[2];
asyncFunction.call(this,title, href, function success(){}, function fail(){} );
}
Upvotes: 1