javascript – 在不超出堆栈限制的情况下迭代或递归大量函数的最佳方法是什么?

前端之家收集整理的这篇文章主要介绍了javascript – 在不超出堆栈限制的情况下迭代或递归大量函数的最佳方法是什么?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个应用程序,我正在Node.js编写,需要进行大量的配置和数据库调用,以处理用户数据.我遇到的问题是,在11,800个函数调用之后,Node将抛出一个错误退出该进程.

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

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

我完全愿意承认,如果是这种情况,我可能会做一些非最优的事情,但如果有更优化的方式,我会很感激.

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

  1. app.post('/initspeaker',function(req,res) {
  2. // if the Admin ID is not present ignore
  3. if(req.body.xyzid!=config.adminid) {
  4. res.send( {} );
  5. return;
  6. }
  7.  
  8. var gcnt = 0,dbsize = 0,goutput = [],goutputdata = [],xyzuserdataCallers = [];
  9.  
  10. xyz.loadbatchfile( xyz.getbatchurl("speakers","csv"),function(data) {
  11. var parsed = csv.parse(data);
  12. console.log("lexicon",parsed[0]);
  13.  
  14. for(var i=1;i<parsed.length;i++) {
  15. if(typeof parsed[i][0] != 'undefined' && parsed[i][0]!='name') {
  16. var xyzevent = require('./lib/model/xyz_speaker').create(parsed[i],parsed[0]);
  17. xyzevent.isPresenter = true;
  18. goutput.push(xyzevent);
  19. }
  20. }
  21. dbsize = goutput.length;
  22.  
  23. xyzuserdataCallers = [new xyzuserdata(),new xyzuserdata(),new xyzuserdata()
  24. ];
  25. // insert all Scheduled Items into the DB
  26. xyzuserdataCallers[0].sendSpeakerData(goutput[0]);
  27. for(var i=1;i<xyzuserdataCallers;i++) {
  28. xyzuserdataCallers[i].sendSpeakerData(8008);
  29. }
  30.  
  31. //sendSpeakerData(goutput[0]);
  32. });
  33.  
  34. var callback = function(data,func) {
  35. //console.log(data);
  36. if(data && data!=8008) {
  37. if(gcnt>=dbsize) {
  38. res.send("done");
  39. } else {
  40. gcnt++;
  41. func.sendSpeakerData(goutput[gcnt]);
  42. }
  43. } else {
  44. gcnt++;
  45. func.sendSpeakerData(goutput[gcnt]);
  46. }
  47. };
  48.  
  49. // callback loop for fetching registrants for events from SMW
  50. var xyzuserdata = function() {};
  51. xyzuserdata.prototype.sendSpeakerData = function(data) {
  52. var thisfunc = this;
  53.  
  54. if(data && data!=8008) {
  55. //console.log('creating user from data',gcnt,dbsize);
  56. var userdata = require('./lib/model/user').create(data.toObject());
  57. var speakerdata = userdata.toObject();
  58. speakerdata.uid = uuid.v1();
  59. speakerdata.isPresenter = true;
  60.  
  61. couchdb.insert(speakerdata,config.couch.db.user,function($data) {
  62. if($data==false) {
  63. // if this fails it is probably due to a UID colliding
  64. console.log("*** trying user data again ***");
  65. speakerdata.uid = uuid.v1();
  66. arguments.callee( speakerdata );
  67. } else {
  68. callback($data,thisfunc);
  69. }
  70. });
  71. } else {
  72. gcnt++;
  73. arguments.callee(goutput[gcnt]);
  74. }
  75. };
  76.  
  77. });

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

>我正在使用Express.js托管的CouchDB,这是响应POST请求
>有一个CSV解析器类可以加载一个驱动扬声器数据的事件列表
>每个活动可以有n个用户(目前所有活动的用户约为8K)
>我正在使用一种模式,在尝试解析任何数据/用户之前加载所有数据/用户
>每个加载的用户(外部数据源)都会转换为我可以使用的对象并进行清理(条带斜线等)
>然后将每个用户插入到CouchDB中

代码在应用程序中有效,但过了一段时间后,我收到错误消息,说已经进行了超过11,800次调用并且应用程序中断了.这不是包含堆栈跟踪的错误,如果它是代码错误就会看到它,由于调用次数正在退出.

再次,任何协助/评论/指示将不胜感激.

解决方法

它看起来像xyzuserdata.sendSpeakerData&正在递归使用回调以保持DB调用顺序.在某些时候你用完了电话堆栈……

有几个模块可以简化串行执行,如StepFlow-JS.

Flow-JS甚至还具有一个便利功能,可以在数组元素上串行应用函数

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

我使用flow.serialForEach编写了一个小测试程序,但遗憾的是能够获得超出最大调用堆栈大小的错误 – 看起来像Flow-JS以类似的方式使用调用堆栈来保持同步.

另一种不构建调用堆栈的方法是避免递归并使用超时值为0的setTimeout来调度回调调用.看到
http://metaduck.com/post/2675027550/asynchronous-iteration-patterns-in-node-js

您可以尝试使用替换回调调用

  1. setTimeout(callback,[$data,thisfunc])

猜你在找的JavaScript相关文章