How can I ensure that THREE.TextureLoader.load() has completed before proceeding?

Currently, I am working on release R73 and my task involves populating an array with materials for later use. The successful loading of all materials in this array is crucial for their intended usage.

At the moment, I am iterating through a JSON information array and executing the following code for each element:

TLoader.load(
        BASE_ODB_URL + jsonMat.pic,
        function (texture) {
            texture.wrapS = THREE.RepeatWrapping;
            texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.set(jsonMat.scaleu, jsonMat.scalev);
            Mat = new THREE.MeshLambertMaterial({
                    map : texture,
                    side : THREE.DoubleSide,
                    name : jsonMat.mname
                });
            THREEMatList.push(Mat);
        },
        function (xhr) {
        }, //onProgress
            function (xhr) {
            Mat = new THREE.MeshLambertMaterial({
                    color : 0xff0000,
                    side : THREE.DoubleSide,
                    name : jsonMat.mname
                });
            THREEMatList.push(Mat);
        } 
    )

The TLoader is instantiated at the beginning: variable TLoader = new THREE.TextureLoader();

If a material is not available when required, a fallback material is used as an error handling mechanism. Is there a method to ensure that .load() completes before proceeding?

Answer №1

Instead of waiting for all assets to load in Threejs, you can use a custom LoadingManager to listen for specific subsets of assets being loaded. Threejs already has a callback for elements loaded via the LoadingManager:

import {TextureLoader, LoadingManager} from './three.module.js';

const getTextures = ()=> new Promise((resolve, reject)=>{
  const manager = new LoadingManager(()=>resolve(textures));
  const loader = new TextureLoader(manager);
  const textures = [
    "image1.jpg",
    "image2.jpg",
    "image3.jpg"
  ].map(filename=>loader.load(filename));
});

getTextures().then(result=>console.log("We received,", result,"!"));

Answer №2

If you find yourself needing to load multiple textures before rendering your scene, a simple helper function can come in handy:

    /**
     *
     * @param {Array} textureSources - An array of strings representing the texture sources
     * @returns {Array} An array containing a Promise for each source 
     */
    function fetchTextures (textureSources) {
        const loader = new THREE.TextureLoader()
        return textureSources.map(textureSource => {
            return new Promise((resolve, reject) => {
                loader.load(
                    textureSource,
                    texture => resolve(texture),
                    undefined, // onProgress callback not supported from r84
                    error => reject(error)
                )
            })
        })
    }

To efficiently handle the loading of textures and catch any errors, wrap your code with Promise.all.

Here's an example:

const basePath = '../assets/textures/'
const src = [
    'image1.jpg',
    'image2.jpg',
    'image3.jpg',
].map(texture => basePath + texture)

Promise.all(fetchTextures(src))
    .then(textures => {
        // Create materials, mesh objects...
        // Render the scene
    })
    .catch(error => console.error(error))

Answer №3

To tackle this issue, one effective approach is to utilize Promises. Promises are considered the way forward and it would have been more convenient if TextureLoader inherently returned a Promise. Nonetheless, it's important to note that IE11 requires a polyfill due to its lack of native promise support.

You can achieve the desired outcome by following a structure similar to this, where textureArray represents your JSON data stored in an iterable Array:

var allPromises = [];

textureArray.forEach( function( jsonMat ) {

    allPromises.push( new Promise( function( resolve, reject ) {

        TLoader.load(
           BASE_ODB_URL + jsonMat.pic,

           function( texture ) {
               // Success callback of TextureLoader
               texture.wrapS = THREE.RepeatWrapping;
               texture.wrapT = THREE.RepeatWrapping;
               texture.repeat.set( jsonMat.scaleu, jsonMat.scalev );
               var material = new THREE.MeshLambertMaterial({
                   map: texture,
                   side: THREE.DoubleSide,
                   name: jsonMat.mname
               });
               THREEMatList.push( material );

               // Notify the promise that it has completed
               resolve( material );
           },

           function( xhr ) {
               // Progress callback of TextureLoader
               // ...
           },

           function( xhr ) {
               // Failure callback of TextureLoader
               // Reject the promise with the failure
               reject( new Error( 'Could not load ' + jsonMat.pic ) );
           }
        );

    }));

});

Promise.all( allPromises )
    .then( function( arrayOfMaterials ) {
        // All textures are now loaded, and this array contains all the materials you created
    }, function( error ) {
        console.error( "Could not load all textures:", error );
    });

The concept here involves utilizing an overarching Promise to monitor the status of other individual promises. Each texture loading process is encapsulated within a Promise added to allPromises. Ultimately, the set of promises is evaluated for success or failure, providing insight into overall completion status.

It's essential to understand that Promise.all succeeds only when all textures are loaded; even a single failure will lead to overall failure. If more precise control is needed, consider using a Promise polyfill like RSVP.js. Alternatively, instead of rejecting the promise, resolving it and handling the result gracefully may be an option.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to iterate through form data utilizing serialization with jQuery and Ajax methods?

I'm struggling to retrieve the post id and store it in the database after a user clicks on the add to favorite button. The issue I'm facing is that all the posts are returning id 30, which happens to be the last id in the database. Here is my P ...

ng-if directive does not show data in AngularJS

I have a dynamic collection of images and videos that I want to display one at a time. Specifically, when I click on an image ID, I want it to show the corresponding image, and when I click on a video ID, I want it to show the relevant video. Below is the ...

Exported excel files cannot contain multiple footer rows in Datatable

Currently, I'm working on creating a Datatable Excel file that can support multiple footers along with an export option. However, I've run into an issue where the footer is only being generated in a single cell without support for multiple lines ...

The button with type Submit inside the form is failing to trigger the Form Submit Event

Having an issue with a custom LoadingButton component in my React TypeScript project using Material-UI (MUI) library. The problem occurs when using this LoadingButton within a form with an onSubmit event handler. The LoadingButton has type="submit&quo ...

A guide on invoking a JavaScript function after receiving a response from an AJAX request

I am facing an issue with a $.POST call that returns the name of a function to be executed, however, the function is not being triggered and I'm unsure why. Below is an example: JavaScript file snippet: $(function(){ $.post('test.php&apos ...

Customize Google Chrome to display a vibrant splash screen instead of a boring white blank page when

"I've been on the lookout for Chrome apps that can help make my screen darker or inverted to reduce eye strain. While I have found some apps that do the job, there's one thing they don't seem to be able to override - the White Blank page. W ...

Confusion Arises When Joomla Buttons Clash

I am facing an issue with two modules on my website, each containing a button code. The code for the buttons is as follows: First Button <div onclick="parent.location='first-link'" data-mce-onclick=""><button class="button">First Bu ...

Determining the dimensions of a div once it has completed loading

Is there a way to retrieve the width and height of a div after it has fully loaded, rather than before? I am currently using JavaScript to get the dimensions, but it seems to be returning the values before the image has finished loading. How can I rectify ...

Tips for effectively interpreting a json string

Trying to extract specific fields from a JSON string and display them on an HTML page, but encountering the following error: SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data This is the JSON being sent: { "Order0& ...

Aligning pixels on a webgl canvas to correspond with the faces of a mesh

My current project involves a hexagonal mesh on the XY plane where I draw a landscape that is pseudo-randomly generated. https://i.sstatic.net/gZ2yR.png To determine if each face should be water or land, I analyze the white and black pixels per face. If ...

Unexpected behavior observed with negated character: ? ^

I am looking to create a form where users can input their phone number and have the flexibility to choose how they want to separate the numbers. I have been using a regex pattern for validation: var regex = /[^0-9 \/-\\\(\)\+ ...

Toggle the visibility of a div using jQuery repeatedly

I'm looking to implement a show/hide functionality using jQuery for a DIV based on a text link. What sets my requirement apart from the examples I've seen on this site is that I need a way to do this multiple times on one page and also have it wo ...

retrieve information from various components identified by the common class name by employing ajax requests

I have a component labeled with the class tclick indicating a sample class <label class="btn btn-default tclick" data-tloc="value1" data-tkey="key1" > <label class="btn btn-default tclick" data-tloc="value2" data-tkey="key2" > <label class= ...

Accessing information from Next.js API endpoint in a Next.js web application

Currently, I am in the process of developing a web application using Next.js APP Router for both the frontend and backend components. The frontend takes care of rendering the user interface, while the backend comprises API routes. I require some guidance o ...

What is the best way to retrieve router parameters within a JSX component?

How can I pass the post ID as a prop to the EditPost component in order to edit it? render() { return ( <Router> <Switch > <Route path="/edit/:id"> <EditPost index={/*what do I do here?*/} /> ...

Unable to load page redirection

I am experiencing an issue with this page not redirecting to the appropriate mobile or desktop page when accessed. Below is the code snippet in question: <html> <head> <title>Loading...</title> </head> < ...

Click on the button to reveal the hidden content within the div, and then smoothly scroll down to view

I have a footer div that is present at the bottom of every page on our site. I've added a button to expand this div, but I'm looking for a way to automatically scroll the page down so that the user can view the expanded content without manually s ...

What is the best way to retrieve the current CSS width of a Vue component within a flexbox layout grid after it has been modified?

There's something about this Vue lifecycle that has me scratching my head. Let me simplify it as best I can. I've got a custom button component whose size is controlled by a flex grid container setup like this: <template> < ...

Combine two entities to create a new entity that mirrors the structure of the first entity

Can someone assist me with a data structure issue? Here are the objects I am working with: const elements = { item1: {color: 'blue', name: 'Item One' }, item2: {color: 'green', name: 'Item Two' }, item3: {colo ...

A guide on retrieving values from programmatically created input elements in Java

Here's the HTML code that I am currently working with: <form method="get" action="#" id="startForm"> <input type="text" name="period" id="period" placeholder="The number of days"/> <input type="submit" name="submit" value="subm ...