0

I'm trying to build a very simple interactive movie player on js just for fun. wireTo(x) triggers every scene by its own duration which predefined on same index. (This is my expectation)

I created a loop and put the setTimeout function inside of it. It iterates without problem on each duration property, but it couldn't handle with name properties(jumps to the last one).

var MoviePlayer = (function() {

  function MoviePlayer(scenes) {
    this.setSource(scenes);
   
  }
  
  MoviePlayer.prototype.setSource = function(_scenes) {
    this.scenes = _scenes;
  }
  
  MoviePlayer.prototype.wireTo = function(number) {
    var parts = this.scenes[number].parts;
   
    for (var x in parts) { 
      var name = parts[x].name; // I think the problem starts here

      setTimeout(function() {
         alert(name); 
      }, parts[x].duration * x);
    }

  }
  
  return MoviePlayer;

}()); 


// scenes configuration json
var scenes = [
  {
    ep: 1,
    parts: [
      { name: "episode 1 p1", duration: 1000 },
      { name: "episode 1 p2", duration: 3000 }
    ],
    next: 2
  },
  {
    ep: 2,
    parts: [
      { name: "episode 2 p1", duration: 1000 },
      { name: "episode 2 p2", duration: 1000 }
    ],
    next: 3
   
  }
];

// instantiation
let player = new MoviePlayer(scenes);
player.wireTo(0);

What's the problem with my logic. Any help would be very appreciated. Thanks.

bgul
  • 136
  • 7
  • https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example – epascarello Feb 13 '19 at 15:26
  • 1
    Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Nicholas Tower Feb 13 '19 at 15:27
  • 1
    You can use [Array.prototype.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) as well or use `let` as in the answer. – HMR Feb 13 '19 at 15:29

1 Answers1

0

Because vars are scoped to the function, you have just a single name variable which you're using for every iteration in the loop. So you keep overwriting the variable, and when the timers eventually go off, they all see the last value you overwrote it with.

Simplest fix is to just use let instead of var. Let is scoped to the block instead of the function, and so each time through the for loop you'll have a new variable with its own binding.

for (let x in parts) { 
  let name = parts[x].name;

  setTimeout(function() {
     alert(name); 
  }, parts[x].duration * x);
}
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • Thanks for the important information about let and var scope difference. Now it's working. – bgul Feb 13 '19 at 15:31