Reputation: 705
Here's my code, I'm trying to make the loadCone()
synchronous with await/async but it isn't working.
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import conePath from "../../static/cone.glb";
import coneBaked from "../../static/coneBake.png";
import "regenerator-runtime/runtime";
export default class Cone {
constructor() {
this.cone;
const textureLoader = new THREE.TextureLoader();
const bakedTexture = textureLoader.load(coneBaked);
const bakedMaterial = new THREE.MeshBasicMaterial({
map: bakedTexture,
});
this.loadCone();
}
async loadCone() {
// GLTF loader
const gltfLoader = new GLTFLoader();
const cone = await gltfLoader.load(
conePath,
(gltf) => {
console.log("success");
console.log(gltf.scene);
this.cone = gltf.scene;
},
(progress) => {
// console.log("progress");
// console.log(progress);
},
(error) => {
console.log("error");
console.log(error);
}
);
}
getCone() {
return this.cone;
}
}
In another file I have this:
const cone = new Cone();
this.scene.add(cone.getCone());
However, because getCone()
is synchronous, it executes before my loadCone()
is done loading the cone, so I get undefined
back instead of the cone.
How do I make my function asynch loadCone()
be synchronous? I tried *.loadAsync()` but it didn't change anything.
Thanks in advance. I am getting no errors other than the fact it says "undefined is not instance of THREE.Object.3d" which I expected it to say.
Upvotes: 2
Views: 3539
Reputation: 62676
I don't know three.js, but new
needs to synchronously return an initialized object, so it cannot be (or invoke something else that is) asynchronous.
// ...
constructor() {
this.cone;
const textureLoader = new THREE.TextureLoader();
const bakedTexture = textureLoader.load(coneBaked);
const bakedMaterial = new THREE.MeshBasicMaterial({
map: bakedTexture,
});
// don't call an async method here
// this.loadCone();
}
//...
On the caller side...
import Cone from 'path to js that defines the class'
// inside an async function
async function doConeStuff() {
const cone = new Cone();
await cone.loadCone();
this.scene.add(cone.getCone());
}
// or, at the top level
const cone = new Cone();
cone.loadCone().then(() => {
this.scene.add(cone.getCone())
});
Noticing that loadCone
is mixing up promises and callbacks, we can either promisify the version you're using or take @Leon.Z's suggestion about the promise-returning version. If that one works (again, I don't know three.js), then the cone class can be simplified like this...
export default class Cone {
constructor() {
const textureLoader = new THREE.TextureLoader();
const bakedTexture = textureLoader.load(coneBaked);
const bakedMaterial = new THREE.MeshBasicMaterial({
map: bakedTexture,
});
this.cone = null;
}
async loadCone() {
// GLTF loader
if (this.cone) Promise.resolve(this.cone);
const giltf = await gltfLoader.loadAsync( 'model.gltf' ),
this.cone = gilt.scene;
return this.cone;
}
getCone() { return this.cone; }
}
Finally, if for some reason, @Leon.Z is incorrect about the promise method, here's how you'd promisify the loader...
async loadCone() {
const gltfLoader = new GLTFLoader();
return new Promise((resolve, reject) => {
gltfLoader.load(
conePath,
gltf => resolve(gilt.scene),
error => reject(error)
);
});
}
Upvotes: 2
Reputation: 400
I can't quite understand what you are doing.
I use this successfully:
// Load a glTF resource
async loadGltf(url) {
// console.log('===== start loadGltf async')
let urlString = url.toString() // load doesn't like the url structure; it requires a string and then builds it up again
let gltf = await gltfLoader.loadAsync(urlString)
// console.log('========== end loadGltf')
return gltf
}
From the result, access result.scene
Upvotes: 2