在不超过堆栈限制的情况下,通过大量庞大的函数进行迭代或recursion的最佳方法是什么?

我有一个应用程序,我正在编写Node.js需要进行大量的configuration和数据库调用,以处理用户数据。 我遇到的问题是,在11800多个函数调用之后,Node会抛出一个错误并退出进程。

错误说:RangeError:超过最大调用堆栈大小

我很好奇,如果有其他人有这种情况出现,并知道他们如何处理这个。 我已经开始将我的代码分解成几个额外的工作文件,但即使如此,每次处理数据节点时,都需要触摸2个数据库(最多25次调用更新各种表),并执行一些清理检查。

我完全愿意承认,如果情况确实如此,我可能会做一些非最佳的事情,但是如果有一个更优化的方式,我会欣赏一些指导。

以下是我在数据上运行的代码示例:

app.post('/initspeaker', function(req, res) { // if the Admin ID is not present ignore if(req.body.xyzid!=config.adminid) { res.send( {} ); return; } var gcnt = 0, dbsize = 0, goutput = [], goutputdata = [], xyzuserdataCallers = []; xyz.loadbatchfile( xyz.getbatchurl("speakers", "csv"), function(data) { var parsed = csv.parse(data); console.log("lexicon", parsed[0]); for(var i=1;i<parsed.length;i++) { if(typeof parsed[i][0] != 'undefined' && parsed[i][0]!='name') { var xyzevent = require('./lib/model/xyz_speaker').create(parsed[i], parsed[0]); xyzevent.isPresenter = true; goutput.push(xyzevent); } } dbsize = goutput.length; xyzuserdataCallers = [new xyzuserdata(), new xyzuserdata(), new xyzuserdata(), new xyzuserdata(), new xyzuserdata(), new xyzuserdata(), new xyzuserdata(), new xyzuserdata() ]; // insert all Scheduled Items into the DB xyzuserdataCallers[0].sendSpeakerData(goutput[0]); for(var i=1;i<xyzuserdataCallers;i++) { xyzuserdataCallers[i].sendSpeakerData(8008); } //sendSpeakerData(goutput[0]); }); var callback = function(data, func) { //console.log(data); if(data && data!=8008) { if(gcnt>=dbsize) { res.send("done"); } else { gcnt++; func.sendSpeakerData(goutput[gcnt]); } } else { gcnt++; func.sendSpeakerData(goutput[gcnt]); } }; // callback loop for fetching registrants for events from SMW var xyzuserdata = function() {}; xyzuserdata.prototype.sendSpeakerData = function(data) { var thisfunc = this; if(data && data!=8008) { //console.log('creating user from data', gcnt, dbsize); var userdata = require('./lib/model/user').create(data.toObject()); var speakerdata = userdata.toObject(); speakerdata.uid = uuid.v1(); speakerdata.isPresenter = true; couchdb.insert(speakerdata, config.couch.db.user, function($data) { if($data==false) { // if this fails it is probably due to a UID colliding console.log("*** trying user data again ***"); speakerdata.uid = uuid.v1(); arguments.callee( speakerdata ); } else { callback($data, thisfunc); } }); } else { gcnt++; arguments.callee(goutput[gcnt]); } }; }); 

这里定义了几个类和项目,需要一些介绍:

  • 我正在使用Express.js +托pipeCouchDB,这是响应POST请求
  • 有一个CSVparsing器类加载驱动拉扬声器数据的事件列表
  • 每个事件可以有n个用户(目前大约8K用户的所有事件)
  • 我正在使用一种模式来加载所有的数据/用户之前试图parsing他们中的任何一个
  • 每个加载的用户(外部数据源)都被转换成一个我可以使用的对象,也可以进行消毒(带斜线等)
  • 然后将每个用户插入到CouchDB中

此代码在应用程序中工作,但过了一段时间后,我收到一个错误,说有超过11,800+的电话已经打了,应用程序中断。 这不是一个包含堆栈跟踪的错误,就像会看到代码错误,由于正在进行的调用次数而退出。

再次,任何援助/评论/方向将不胜感激。

它看起来像xyzuserdata.sendSpeakerData和callback正在recursion使用,以保持DB调用顺序。 在某个时候,你用尽了通话堆栈…

有几个模块可以简化串行执行,如Step或Flow-JS 。

Flow-JS甚至有一个方便的function来对数组元素串行应用一个函数:

 flow.serialForEach(goutput, xyzuserdata.sendSpeakerData, ...) 

我使用flow.serialForEach编写了一个小testing程序,但不幸的是能够获得Maximum call stack size exceeded错误 – 看起来像Flow-JS使用调用堆栈以类似的方式保持同步。

另一种不构build调用堆栈的方法是避免recursion,并使用超时值为0的setTimeout来调度callback调用。 见http://metaduck.com/post/2675027550/asynchronous-iteration-patterns-in-node-js

您可以尝试用replacecallback呼叫

 setTimeout(callback, 0, [$data, thisfunc]) 

recursion对同步asynchronous操作非常有用 – 这就是为什么在flow.js中使用它的原因

但是,如果您想要处理数组中无限数量的元素或缓冲stream,则需要使用node.js的事件发射器。

伪代码中

  ee = eventemitter arr = A_very_long_array_to_process callback = callback_to_call_once_either_with_an_error_or_when_done // the worker function does everything processOne() { var next = arr. shift(); if( !arr ) ee.emit ( 'finished' ) return process( function( err, response) { if( err ) callback( err, response ) else ee.emit( 'done-one' ) } ); } // here we process the final event that the worker will throw when done ee.on( 'finished', function() { callback( null, 'we processed the entire array!'); } ); // here we say what to do after one thing has been processed ee.on( 'done-one', function() { processOne(); } ); // here we get the ball rolling processOne();