Javascript Node.js web爬虫:超过最大调用堆栈

Javascript Node.js web爬虫:超过最大调用堆栈,javascript,node.js,Javascript,Node.js,我是node.js的新手,我正在试图弄清楚为什么会出现“超过最大调用堆栈”错误 这是一个有3个部分的网络爬虫 标准事件发射器 函数-doRequest(url) 获取请求结果(作为事件发出)并相应更新数据库的管理器 由于都是异步调用,因此管理器最多会同时调用doRequest一组次。当通过发出事件完成请求时,它会记录结果,从废弃url的数据库中获取下一个url,然后将其作为可用url发出。这将导致管理器再次调用doRequest函数 我真的很不明白为什么这会给我一个最大调用堆栈超出的问题。 我还

我是node.js的新手,我正在试图弄清楚为什么会出现“超过最大调用堆栈”错误

这是一个有3个部分的网络爬虫

  • 标准事件发射器
  • 函数-doRequest(url)
  • 获取请求结果(作为事件发出)并相应更新数据库的管理器
  • 由于都是异步调用,因此管理器最多会同时调用doRequest一组次。当通过发出事件完成请求时,它会记录结果,从废弃url的数据库中获取下一个url,然后将其作为可用url发出。这将导致管理器再次调用doRequest函数

    我真的很不明白为什么这会给我一个最大调用堆栈超出的问题。 我还加入了那些console.trace()参数,跟踪的调用堆栈都没有那么大

    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);