Javascript Node.js web爬虫:超过最大调用堆栈
我是node.js的新手,我正在试图弄清楚为什么会出现“超过最大调用堆栈”错误 这是一个有3个部分的网络爬虫Javascript Node.js web爬虫:超过最大调用堆栈,javascript,node.js,Javascript,Node.js,我是node.js的新手,我正在试图弄清楚为什么会出现“超过最大调用堆栈”错误 这是一个有3个部分的网络爬虫 标准事件发射器 函数-doRequest(url) 获取请求结果(作为事件发出)并相应更新数据库的管理器 由于都是异步调用,因此管理器最多会同时调用doRequest一组次。当通过发出事件完成请求时,它会记录结果,从废弃url的数据库中获取下一个url,然后将其作为可用url发出。这将导致管理器再次调用doRequest函数 我真的很不明白为什么这会给我一个最大调用堆栈超出的问题。 我还
var emitter = new (require('events').EventEmitter);
var inqueue = 0;
function doRequest(url) {
console.trace();
setImmediate(function(){
request(url, function (error, response, html) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(html);
$('a').each(function(i, element){
var a = $(this);
if(a.attr("href")) {
if(a.attr('href').indexOf("http://")!= -1){
var url = a.attr('href');
// Our parsed meta data object
setImmediate(function(){emitter.emit("gotUrl", url)},inqueue);
} /*
links without an http:// in their href
are useless to us. They are either buttons or relative links
to sites we can't buy as only a section of the site is dead.
*/
} else {
//link that isn't a link.
}
});
setImmediate(function(){emitter.emit("scraped", url)});
} else {
setImmediate(function(){emitter.emit("dead",url)});
}
return true;
});
}, 500);
}
module.exports = function() {
console.log("built");
Error.stackTraceLimit= Infinity;
var maxthreads = 1;
var running = 0;
var urls = [];
var doneurls = [];
var excluderegex = / /;
var index = 0;
var ORM = null;
var total = 0;
var storage = null;
var visited = null;
var queuelinks = null;
var cleanupThreshold = 5;
var cleanupMin = 3
var timeout = 0; //set to zero for initial pull, then moved to 500 in doRequest.
var theManager = this;
this.logTheFallen = function(url) {
storage.create({url:url}).success(function(){
console.log("found dead link:"+url);
setImmediate(function(){emitter.emit("available")});
});
};
this.setUrls = function(arr) {
console.log(arr);
queuelinks.create({url:arr[0]}).success(function(q){
console.log("Queued again: "+q.url);
setImmediate(theManager.run);
});
};
this.setExcluders = function(exclude) {
excluderegex = exclude;
}
this.setOrm = function(zOrm) {
ORM = zOrm;
}
this.setStorage = function(model) {
storage = model;
}
this.setVisited = function(model) {
visited = model;
}
this.setQueue = function(model) {
queuelinks = model;
}
this.setCleanupThreshold = function(thres){ cleanupThreshold = thres};
this.threadfinished = function(){
queuelinks.count().success(function(count){
console.log("There are "+count+" that have been added to the queue. On index:"+index);
//DO NOT mess with this synchronous emit. It makes things fail for some reason.
});
emitter.emit("available");
while(running<maxthreads) {
running++;
setImmediate(function(){emitter.emit("available")});
}
};
this.getUrl =function() {
inqueue++;
console.trace()
console.log("getting url");
index++;
queuelinks.find({where:{id:index}}).success(function(item){
console.log("success");
console.trace();
if(item){
console.log("found item");
setImmediate(function(){emitter.emit("newurl", item.url)});
} else {
index--;
console.log("expended");
setImmediate(function(){emitter.emit("expended")});
}
}).error(function(err){
console.log(err);
});
};
this.addToQueue =function(zurl) {
console.log("queueing link"+zurl);
queuelinks.findOrCreate({url:zurl}).success(function(item){
console.trace();
inqueue--;
});
};
this.logUrl = function(href) {
//console.log(href);
};
this.newThread = function() {
console.log("Launching a new request");
console.trace();
running++;
setImmediate(function(){theManager.getUrl()});
};
this.run = function() {
console.log("running");
var ind = 1;
function launchThread(ind){
queuelinks.find({where:{id:ind}}).success(function(queued){
if(queued) {
ind++;
console.log("launching thread...");
newThread();
launchThread(ind);
}
});
console.log(ind);
};
setImmediate(function(){launchThread(ind)});
}
emitter.on("dead", function(url){
setImmediate(function(){theManager.logTheFallen(url)});
});
emitter.on("newurl", function(url){
console.log("got new url:"+url);
setImmediate(function(){doRequest(url)});
});
emitter.on("gotUrl", function(url){
setImmediate(function(){theManager.addToQueue(url)});
});
emitter.on("scraped", function(url){
var zUrl = new String(url);
setImmediate(function(){
console.trace();
visited.findOrCreate({url:url}).success(function(visited) {
if(visited.created){
console.log("added visited link");
} else{
console.log("already added");
setImmediate(theManager.threadfinished)
}
});
},0);
});
emitter.on("available", function(){
console.log("launching thread");
console.trace();
theManager.newThread();
});
emitter.on("expended", function() {
console.log("expended");
});
return this;
}
var-emitter=new(需要('events').EventEmitter);
var-inqueue=0;
函数doRequest(url){
console.trace();
setImmediate(函数(){
请求(url、函数(错误、响应、html){
如果(!error&&response.statusCode==200){
var$=cheerio.load(html);
$('a')。每个(函数(i,元素){
var a=$(本);
如果(a.attr(“href”)){
if(a.attr('href').indexOf(“http:/”)!=-1){
var url=a.attr('href');
//我们解析的元数据对象
setImmediate(函数(){emitter.emit(“gotUrl”,url)},inqueue);
} /*
在其href中没有http://的链接
对我们来说是无用的。它们要么是按钮,要么是相对链接
对于我们无法购买的网站,只有一部分网站已经死亡。
*/
}否则{
//不是链接的链接。
}
});
setImmediate(函数(){emitter.emit(“scraped”,url)});
}否则{
setImmediate(函数(){emitter.emit(“dead”,url)});
}
返回true;
});
}, 500);
}
module.exports=函数(){
控制台日志(“构建”);
错误。stackTraceLimit=无穷大;
var-maxthreads=1;
var运行=0;
var url=[];
var doneurls=[];
var excluderegex=/;
var指数=0;
var-ORM=null;
var合计=0;
var存储=null;
var=null;
var queuelinks=null;
var cleanupThreshold=5;
var cleanupMin=3
var timeout=0;//初始拉取设置为零,然后在doRequest中移动到500。
var theManager=此;
this.logoffallen=函数(url){
创建({url:url}).success(函数(){
log(“发现死链接:+url”);
setImmediate(函数(){emitter.emit(“可用”)});
});
};
this.setUrls=函数(arr){
控制台日志(arr);
queuelinks.create({url:arr[0]}).success(函数(q){
log(“再次排队:+q.url”);
setImmediate(manager.run);
});
};
this.setExcluders=函数(排除){
excluderegex=排除;
}
this.setOrm=函数(zOrm){
ORM=zOrm;
}
this.setStorage=功能(型号){
存储=模型;
}
this.setVisited=函数(模型){
访问量=模型;
}
this.setQueue=函数(模型){
队列链接=模型;
}
this.setCleanupThreshold=函数(thres){cleanupThreshold=thres};
this.threadfinished=函数(){
queuelinks.count().success(函数(计数){
log(“有”+count+”已添加到队列中。在索引上:“+index”);
//不要弄乱这个同步发射。它会由于某种原因使事情失败。
});
发射器。发射(“可用”);
当(自然地)运行时,只要我发布到,我就知道了。就我所知,这是给那些追求它的人的交易
如果订阅者将内容推送到发布者,任何类型的发布/订阅行为(如事件)都可能发生这种情况,这最终将创建一个链。发出的事件及其响应仍然有一个调用堆栈序列-它们不像我认为的那样真正异步
因此,简单的答案是:
emitter.on("yourEvent", function(){
setTimeout(yourFunction(), 0);
})
这将清除调用堆栈并创建一个新的调用堆栈。TADA!如果没有代码,很难判断。您可以发布代码吗?您的代码或您的npm模块使用process.nextTick而不是setImmediate,并且您运行上面的节点v0.10。@wayne不,我使用的是v10,而不是process.nextTick。我只是使用setTimeout(func,0).奇怪的是我用的是console.trace()“而且没有看到调用堆栈的增长。”Blender发布。我一直在尝试解决这个问题,所以有一些未使用的变量,因此我需要清理。我所做的一切似乎都没有任何影响。而且..这只起到了帮助作用。我现在无法理解这是怎么回事。这是永远不会被调用的,即使它应该是:setTimeout(函数(){emitter.emit(“newurl”,url);},0);