setInterval调用ajax报too much recursion递归错误排查与解决
问题记录与解决
大家好:
更新说明
我已通过变通方案解决该问题,将原有循环查询子表数据写入数组的逻辑:
$numcflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numcflinfparr['moveable_id']); while($numcflsarr = mysqli_fetch_assoc($numcflsinfpres)) { array_push($numcflinfparr['moveable_subs'],$numcflsarr); }
封装为独立函数,改用如下调用方式实现:
array_push($numcflinfparr['moveable_subs'], get_babylon_sub_object_data($numcflinfparr['moveable_id']));
问题原描述
我需要通过jQuery的$.ajax方法接收PHP端mysqli_fetch_assoc返回的数组,实现前端JS对象的数据更新。该功能原本运行正常,但在添加setInterval配置每15分钟自动更新数据后,控制台抛出 Uncaught (in promise) InternalError: too much recursion 错误。
我曾尝试将原for..of全量遍历更新逻辑改为仅针对指定objectID调用单个对象更新接口,仍未解决问题,报错定位在$.ajax语句所在行。经排查,报错在执行PHP函数中向数组push关联数组的逻辑时触发,对应函数代码如下:
function get_babylon_object_data($user,$id) { $envobjects = array(); $envobjectsarr = mysqli_fetch_assoc(dbquery('SELECT object_id,object_user_id FROM '.$db_table['objects'].' WHERE object_id='.$id)); if(($envobjectsarr['object_id'] ?? 0)>0) { $envobjectsarr['object_moveables'] = array(); if($envobjectsarr['object_user_id']==$user['id']) { $numcflinfpres = dbquery('SELECT moveable_id,moveable_user_id,moveable_object_id,moveable_subobject_id FROM '.$db_table['moveable'].' WHERE moveable_object_id='.$envobjectsarr['object_id']); while($numcflinfparr = mysqli_fetch_assoc($numcflinfpres)) { $numcflinfparr['moveable_subs'] = array(); $numcflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numcflinfparr['moveable_id']); while($numcflsarr = mysqli_fetch_assoc($numcflsinfpres)) { array_push($numcflinfparr['moveable_subs'],$numcflsarr); } array_push($envobjectsarr['object_moveables'],$numcflinfparr); } } $envobjectsarr['object_subobjects'] = array(); $envsubobjectsres = dbquery('SELECT * FROM '.$db_table['subobjects'].' WHERE subobject_object_id='.$envobjectsarr['object_id']); while($envsubobjectsarr=mysqli_fetch_assoc($envsubobjectsres)) { $envsubobjectsarr['subobject_moveables'] = array(); if($envsubobjectsarr['subobject_user_id']==$client['id']) { $numflinfpres = dbquery('SELECT moveable_id,moveable_user_id,moveable_object_id,moveable_subobject_id FROM '.$db_table['moveable'].' WHERE moveable_subobject_id='.$envsubobjectsarr['subobject_id']); while($numflinfparr = mysqli_fetch_assoc($numflinfpres)) { $numflinfparr['moveable_subs'] = array(); $numflsinfpres = dbquery('SELECT fs_sub_id,fs_sub_cnt FROM '.$db_table['moveable_subs'].' WHERE fs_moveable_id='.$numflinfparr['moveable_id']); while($numflsinfparr = mysqli_fetch_assoc($numflsinfpres)) { array_push($numflinfparr['moveable_subs'], $numflsinfparr); } array_push($envsubobjectsarr['subobject_moveables'], $numflinfparr); } } $envobjectsarr['object_subobjects'][$envsubobjectsarr['subobject_object_pos']] = $envsubobjectsarr; } $envobjects[$id] = $envobjectsarr; } return $envobjects[$id] ?? array('object_id'=>$id); }
错误调用栈
完整错误调用栈显示jQuery的isPlainObject、extend方法出现上百次递归调用:
aUpdateObject Updater.js:15 updateObject Updater.js:8 interval Updater.js:35 (Async: setTimeout handler) interval Updater.js:32 (Async: setInterval handler) initUpdater Updater.js:30 setup Central.js:29 onFinish Central.js:28 _decreaseWaitingTasksCount assetsManager.ts:1143 done assetsManager.ts:1176 _onDoneCallback assetsManager.ts:168 run assetsManager.ts:117 runTask assetsManager.ts:389 successHandler sceneLoader.ts:756 ImportMesh sceneLoader.ts:792 (Async: promise callback) ImportMesh sceneLoader.ts:789 dataCallback sceneLoader.ts:552 GLTFFileLoader glTFFileLoader.ts:652 LoadFile fileTools.ts:343 onReadyStateChange fileTools.ts:457 (Async: EventListener.handleEvent) addEventListener webRequest.ts:138 retryLoop fileTools.ts:482 requestFile fileTools.ts:485 RequestFile fileTools.ts:519 LoadFile fileTools.ts:342 _loadFile scene.ts:4416 _loadFile glTFFileLoader.ts:866 loadFile glTFFileLoader.ts:647 manifestChecked sceneLoader.ts:582 Database database.ts:69 OfflineProviderFactory database.ts:11 _LoadData sceneLoader.ts:603 ImportMesh sceneLoader.ts:765 runTask assetsManager.ts:379 run assetsManager.ts:114 _runTask assetsManager.ts:1194 load assetsManager.ts:1248 createScene Central.js:36 default Central.js:10 <anonym> index.js:5 InnerModuleEvaluation self-hosted:2325 evaluation self-hosted:2286 >jquery 128 isPlainObject ... (127x?) extend
相关代码文件
Updater.js
export class Updater { constructor(main) { this.objects = {}; this.upobjects = {}; // 更新请求存储对象 } updateObject(id) { if(!this.upobjects[id]) { // 无对应进行中的更新请求时才触发 this.aUpdateObject(id); this.upobjects[id] = id; // 标记请求已发起 } } async aUpdateObject(id) { const aUpdater = this; // 缓存上下文供回调使用 $.ajax({ type: 'post', url: 'path/to/updateData.php', dataType:'json', data: {user:aUpdater.data, id:id}, success : function (result) { // 依次更新数据、3D模型、UI }, error: function(data) { console.log('error'); console.log(data); } }); } initUpdater() { const updater = this; this.interval = window.setInterval(function(){ // 每15分钟执行一次全量更新 let count = 1; for(const object of Object.values(updater.objects)) { // 遍历所有对象 setTimeout(function(){ updater.updateObject(object.id); // 触发单个对象更新 },count*1000); count++; } }, 900000); } }
Central.js
// 引入BABYLON库 // 引入Updater.js export default class { constructor() { this.canvas = document.getElementById('renderCanvas'); // 渲染画布 this.engine = new BABYLON.Engine(this.canvas, true, { stencil: true }); // 初始化渲染引擎 this.scene = new BABYLON.Scene(this.engine); // 初始化场景 this.createScene(); } createScene() { this.assetManager = new BABYLON.AssetsManager(this.scene); // 模型加载器 this.updater = new Updater(this); // 初始化对象更新管理器 // 此处填充this.updater.objects数据,准备模型加载逻辑 const central = this; window.addEventListener('resize', function() { central.engine.resize() }); this.assetManager.onProgress = (remainingCount, totalCount, task) => { console.log(remainingCount, totalCount, task.name); } this.assetManager.onFinish = (tasks) => { // 所有3D模型加载完成后触发 this.setup(); this.engine.runRenderLoop(function() { central.scene.render(); // 启动场景渲染循环 }); }; this.assetManager.load(); // 开始加载3D模型 } setup() { this.updater.initUpdater(); } }
index.js
// 引入BABYLON库 // 引入Central.js if (BABYLON.Engine.isSupported()) { window.central = new Central(); // 初始化应用 }
updateData.php
<?PHP ini_set('MAX_EXECUTION_TIME', 1800); define('CENTRAL_ROOT_DIR','..'); include_once(CENTRAL_ROOT_DIR.'/path/to/funcs.php'); // 包含get_babylon_object_data()方法 include_once(CENTRAL_ROOT_DIR.'/path/to/config.php'); // 包含数据库配置 dbconnect(); // 连接MySQL数据库 $conf = get_all_config(); // 获取应用配置 include_once(CENTRAL_ROOT_DIR.'/path/to/definitions.php'); // 包含全局常量定义 function get_babylon_object($user,$id) { return json_encode(get_babylon_object_data($user,$id)); // 方法通过mysqli_fetch_assoc查询数据库,更新并返回用户关联数组 } echo get_babylon_object($_POST['user'] ?? array(),$_POST['id'] ?? 0); dbclose(); // 关闭数据库连接 ?>
内容的提问来源于stack exchange,提问作者eddzscorpion77gmailcom
相关产品推荐
相关产品推荐

