0

What I'm trying to do is:

  1. Get a JSONfile from server, containing data about my models
  2. Use a PLY loader inside a for loop to add them to the scene
  3. Add them to an array

Here are my functions:

function getJSON(callback) {
    var temp = $.getJSON("data/data.json", function(data) {
        //execute the callback, passing it the data
        callback(data);
    });
}

function loadModels() {

    //get our JSON
    getJSON(function(data) {

        //evaluate data

        nodes = data.library[0].model.nodes;

        nodesLen = nodes.length;

        //Load nodes and add them to scene and array
        for (var i = 0; i < nodesLen; i++) {

            var url = nodes[i].url;

            // PLY loader
            var loader = new THREE.PLYLoader();
            loader.load(url, function(geometry) {

                geometry.computeFaceNormals();

                var material = new THREE.MeshPhongMaterial({ color: 0xffffff, vertexColors: THREE.VertexColors, transparent: true, side: THREE.DoubleSide });
                var mesh = new THREE.Mesh(geometry, material);

                mesh.stepNum = i;

                console.log(i);

                mesh.position.x = 0;
                mesh.position.y = 0;
                mesh.position.z = 0;

                //Add to scene
                scene.add(mesh);

                //Push into array
                nodesArr.push(mesh);

            });
        }

    });

}

Problem: They are not loaded correctly. When I check the output of "console.log(i)" in the PLY loader it return always the same value. I think while it is loading the model another loop has already started. How do I instruct the loop to wait until all functions complete before another loop starts?

leota
  • 1,636
  • 2
  • 13
  • 33
  • this might help you: http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – it's not so obvious, but the javascript-engine does things slightly different than you might think. – Martin Schuhfuß Sep 23 '16 at 20:19

1 Answers1

1

As already mentioned in the comment, this should explain the bahviour quite well: JavaScript closure inside loops – simple practical example

The problem is basically this: the function loader.load() is asynchronous, which means that the function-call will return immediately and won't wait for the response from the network. So, your loop will complete in no time (at which point i equals nodesLen and all HTTP-requests have been initiated). But none of the request is completed by then. This will only happen a long time after that (speaking of microseconds vs hundreds of milliseconds), at which point your callback (function(geometry) {}) is finally called for the first time.

One solution for that is explained in the answer I linked above: you can use a closure to "seal" the current value of i into the callback. This will probably solve your problem, but you still have a lot of concurrent requests going on (also not neccessarily a bad thing, the browser can handle that).

But to answer your main question: If you want to wait with the next request until the previous request was completed, you need to do something like this:

var remainingUrls = [ /* ... list of all model-urls ... */ ];

function loadNext() {
  // get the first of the remaining urls and remove it from the array
  var nextUrl = remainingUrls.shift();

  if (!nextUrl) {
    // when there is no next url, we are done here
    return;
  }

  loader.load(nextUrl, function(geometry) {
    addToScene(geometry);

    // now it's time to start loading the next file
    loadNext();
  });
}

// start loading the whole list
loadNext();
Community
  • 1
  • 1
Martin Schuhfuß
  • 6,814
  • 1
  • 36
  • 44
  • Great solution! This is a real example of how to think out of the box. Very helpful, thanks a lot :) – leota Sep 23 '16 at 21:39