GLTF模型加载时.then()未等待所有Promise完成,出现迭代报错
异步加载GLTF模型时Promise提前resolve导致的问题分析与修复
问题描述
使用async函数加载多个GLTF模型,通过.then()遍历结果并添加到场景,原本运行正常,但移除一个大体积模型后,.then()未等待所有Promise完成就执行,控制台报错:'variant.components is not iterable'。以下是相关代码:
加载器函数
async function modelLoader(source, loadingManager) { const gltfLoader = new GLTFLoader(loadingManager); const promises = []; for (let i = 0; i < source.length; i++) { promises.push( new Promise((resolve) => { const model = {} gltfLoader.load(source[i].file, (gltf) => { const children = [...gltf.scene.children]; console.log(children); model.components = children; resolve(model); }); if (source[i].type === 'baseModel' || (source[i].type === 'variant' && source[i].viewPoints.file !== 'none')) { gltfLoader.load(source[i].viewPoints.file, (gltf) => { const viewPoints = [...gltf.scene.children]; model.viewPoints = viewPoints; resolve(model); }); } else if (source[i].type === 'baseModel' || (source[i].type === 'variant' && source[i].viewPoints.file === 'none')) { model.viewPoints = 'none'; } else if (source[i].type === 'land') { gltfLoader.load(source[i].buildingLine, (gltf) => { const buildingLine = [...gltf.scene.children] model.buildingLine = buildingLine resolve(model) }) } }) ); } return await Promise.all(promises); }
调用代码
const allModels = modelLoader(source, loadingManager) allModels.then(result => { for (const variant of result) { if (variant.type === 'baseModel') { const house = new THREE.Group() for (const components of variant.components) { house.add(components) } } } })
问题根源
- 同一个Promise被多次resolve:对于
baseModel或符合条件的variant类型,主模型加载和viewPoints加载的回调里都调用了resolve(model)。哪个异步请求先完成,就会先触发Promise resolve,此时如果viewPoints加载更快,model.components还未被赋值,后续遍历就会因variant.components不存在报错。 - 逻辑判断冗余:第二个
else if重复判断了source[i].type === 'baseModel',导致逻辑混乱,可能出现不符合预期的属性赋值。 - 部分场景未等待所有加载完成:比如
land类型的模型,仅在buildingLine加载完成后resolve,但主模型加载可能还未完成,同样会导致model.components缺失。
修复方案
重构每个模型的加载逻辑,将当前模型的所有异步加载任务包装成子Promise数组,通过Promise.all等待所有任务完成后再resolve模型对象,确保所有属性都被正确赋值。
修复后的加载器函数
async function modelLoader(source, loadingManager) { const gltfLoader = new GLTFLoader(loadingManager); const promises = []; for (const item of source) { const modelPromise = new Promise(async (resolve) => { const model = {}; const subPromises = []; // 加载主模型 const loadMainModel = new Promise((res) => { gltfLoader.load(item.file, (gltf) => { model.components = [...gltf.scene.children]; res(); }); }); subPromises.push(loadMainModel); // 处理不同类型的额外加载任务 if (item.type === 'baseModel' || item.type === 'variant') { if (item.viewPoints.file !== 'none') { const loadViewPoints = new Promise((res) => { gltfLoader.load(item.viewPoints.file, (gltf) => { model.viewPoints = [...gltf.scene.children]; res(); }); }); subPromises.push(loadViewPoints); } else { model.viewPoints = 'none'; } } else if (item.type === 'land') { const loadBuildingLine = new Promise((res) => { gltfLoader.load(item.buildingLine, (gltf) => { model.buildingLine = [...gltf.scene.children]; res(); }); }); subPromises.push(loadBuildingLine); } // 等待当前模型的所有加载任务完成后再resolve await Promise.all(subPromises); resolve(model); }); promises.push(modelPromise); } return Promise.all(promises); }
关键修复点
- 为每个模型创建子Promise数组,收集主模型、viewPoints、buildingLine等所有异步加载任务。
- 使用
Promise.all等待当前模型的所有加载任务完成,确保model的所有属性都被赋值后再resolve。 - 简化逻辑判断,移除冗余的条件判断,让代码逻辑更清晰。
- 确保无论哪种模型类型,都要等主模型加载完成后才resolve,避免
components属性缺失。
额外优化建议
- 给
gltfLoader.load添加错误回调,处理加载失败的情况,避免Promise永远处于pending状态:gltfLoader.load(item.file, (gltf) => { model.components = [...gltf.scene.children]; res(); }, undefined, (err) => { console.error('主模型加载失败:', err); res(); // 或reject(err),根据业务需求处理 }); - 在调用
.then()时添加catch捕获错误,便于调试:allModels.then(result => { // 处理逻辑 }).catch(err => { console.error('模型加载出错:', err); });
内容的提问来源于stack exchange,提问作者Nico
相关产品推荐
相关产品推荐

