如何让Three.js的loadModels函数返回含所有加载模型的Promise?
修改异步模型加载函数以返回包含所有模型的Promise
问题根源
原代码中GLTFLoader.load采用回调式异步逻辑,函数虽标记为async但未等待任何异步操作完成就直接结束,导致外部无法获取加载完成后的模型数组;同时全局models变量也存在作用域污染风险。
解决方案
将每个模型加载逻辑封装为Promise,通过Promise.all等待所有加载任务完成,最终返回包含所有模型的Promise。具体修改如下:
1. 封装Promise版GLTF加载工具函数
把回调式的loader.load转换为Promise,适配async/await语法:
function loadGLTF(loader, url) { return new Promise((resolve, reject) => { loader.load(url, resolve, undefined, reject); }); }
2. 重构loadModels函数
将每个模型的加载、处理逻辑拆分为独立异步任务,统一收集所有模型后返回:
async function loadModels(lights, roomWidth, roomHeight) { const loader = new GLTFLoader(loadingManager); // 加载并处理灯光模型(含多个克隆体) const loadLight = async () => { const gltf = await loadGLTF(loader, './models/scifi_light_11.glb'); gltf.scene.scale.set(0.02, 0.02, 0.02); gltf.scene.rotateZ(-0.7 * Math.PI); gltf.scene.receiveShadow = true; gltf.scene.castShadow = true; const lightModels = []; // 初始模型 gltf.scene.position.set(-(roomWidth / 2) + 0.32, roomHeight - 0.4, 0); scene.add(gltf.scene); lightModels.push(gltf.scene); // 克隆模型1 const l1 = gltf.scene.clone(true); l1.position.set(-roomWidth / 2 + 0.32, roomHeight - 0.4, -roomWidth); scene.add(l1); lightModels.push(l1); // 克隆模型2 const l2 = gltf.scene.clone(true); l2.position.set(roomWidth / 2 + 0.32, roomHeight - 0.4, -roomWidth); scene.add(l2); lightModels.push(l2); // 克隆模型3 const l3 = gltf.scene.clone(true); l3.position.set(roomWidth / 2 + 0.32, roomHeight - 0.4, -2 * roomWidth); scene.add(l3); lightModels.push(l3); // 克隆模型4 const l4 = gltf.scene.clone(true); l4.position.set(-roomWidth / 2 + 0.32, roomHeight - 0.4, -2 * roomWidth); scene.add(l4); lightModels.push(l4); return lightModels; }; // 加载并处理终端模型 const loadTerminal = async () => { const loader1 = new GLTFLoader(loadingManager); const gltf = await loadGLTF(loader1, './models/terminal/scene.gltf'); let model = gltf.scene; model.traverse(node => { if (node.isMesh) node.castShadow = true; }); model.scale.set(8, 8, 8); model.rotateY(0.6); const randomX = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); const randomZ = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); model.position.set(randomX, 0, randomZ); scene.add(model); return [model]; }; // 加载并处理胶囊模型 const loadCapsule = async () => { const loader2 = new GLTFLoader(loadingManager); const gltf = await loadGLTF(loader2, './models/capsule/scene.gltf'); let model = gltf.scene; model.traverse(node => { if (node.isMesh) node.castShadow = true; }); model.scale.set(0.01, 0.01, 0.01); const randomX = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); const randomZ = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); model.position.set(randomX, 0, -roomWidth + randomZ); model.rotateY(-0.6); lights[1].target = model; scene.add(model); return [model]; }; // 加载并处理立方体模型 const loadCube = async () => { const loader3 = new GLTFLoader(loadingManager); const gltf = await loadGLTF(loader3, './models/cube/scene.gltf'); let model = gltf.scene; model.traverse(node => { if (node.isMesh) node.castShadow = true; }); model.scale.set(8, 8, 8); const randomX = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); const randomZ = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); model.position.set(roomWidth + randomX, 0.2, -roomWidth + randomZ); model.rotateY(0.6); lights[2].target = model; scene.add(model); return [model]; }; // 加载并处理 barrel 模型 const loadBarrel = async () => { const loader4 = new GLTFLoader(loadingManager); const gltf = await loadGLTF(loader4, './models/barrel/scene.gltf'); let model = gltf.scene; model.traverse(node => { if (node.isMesh) node.castShadow = true; }); model.scale.set(0.04, 0.04, 0.04); const randomX = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); const randomZ = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); model.position.set(roomWidth + randomX, 0, -2 * roomWidth + randomZ); model.rotateY(-0.6); lights[3].target = model; scene.add(model); return [model]; }; // 加载并处理反应堆模型 const loadReactor = async () => { const loader5 = new GLTFLoader(loadingManager); const gltf = await loadGLTF(loader5, './models/reactor/scene.gltf'); let model = gltf.scene; model.traverse(node => { if (node.isMesh) node.castShadow = true; }); model.scale.set(0.6, 0.6, 0.6); const randomX = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); const randomZ = Math.random() * (roomWidth / 4 - (-roomWidth / 4)) + (-roomWidth / 4); model.position.set(randomX, 1, -2 * roomWidth + randomZ); model.rotateY(-0.6); lights[4].target = model; scene.add(model); return [model]; }; // 等待所有加载任务完成,合并所有模型数组 const allModelGroups = await Promise.all([ loadLight(), loadTerminal(), loadCapsule(), loadCube(), loadBarrel(), loadReactor() ]); // 扁平化数组,返回一维模型列表 return allModelGroups.flat(); }
3. 使用方式
调用loadModels时,可通过await或.then获取加载完成的所有模型:
// 方式1:在async函数内使用await async function initScene() { try { const allModels = await loadModels(lights, roomWidth, roomHeight); console.log('所有模型加载完成', allModels); // 后续场景初始化逻辑 } catch (err) { console.error('模型加载失败', err); } } // 方式2:使用Promise链式调用 loadModels(lights, roomWidth, roomHeight) .then(allModels => { console.log('所有模型加载完成', allModels); // 后续场景初始化逻辑 }) .catch(err => { console.error('模型加载失败', err); });
关键说明
- 每个模型加载任务拆分为独立异步函数,便于维护和逻辑分离
loadGLTF工具函数统一处理回调转Promise的逻辑,避免重复代码Promise.all确保所有模型加载完成后才返回结果,解决异步时序问题- 最终返回扁平化的一维数组,包含所有原始模型和克隆模型
内容的提问来源于stack exchange,提问作者FlorNeufkens
相关产品推荐
相关产品推荐

