tommica
tommica

Reputation: 132

Own loops for each instantiated class in javascript

I wanted to try out creating a proof-of-concept of actors that have their own independent loops, outside of the main loop - I created something like that, but I'd like to know if there are some glaring issues, or if I am doing it completely wrong.

Basically I'd like to know if the right way to handle the "internal" loop would be using this, or if there is a better way of doing it (inside the live() function): setTimeout(() => {this.live()}, 100);

Second question would be to know the best way of destroying an instantiated class, withing the class, with something like "this.destroy()" - right now I am just removing the connection from the container to the object

Example here: https://codepen.io/tommica/pen/qmNXYL

I'll paste the code itself too:

<ul id="simple-log"></ul>

<script>
// We will store out actors as "id" => "actor"
let actors = {};

// Custom logging functionality for a simple ul list
const sLog = (text) => {
  let li = document.createElement('li');
  li.innerHTML = text;
  document.getElementById('simple-log').appendChild(li);
};

const randomNum = (min,max) => { return Math.floor(Math.random() * max) + min; }

// Actor definition
class Actor {
  constructor(name, id) {
    this.id = id;
    this.name = name;
    this.gender = randomNum(1,2) === 1 ? 'male' : 'female'; // Random gender
    this.lastTime = null;
  }

  live() {
    // Get the current time, and log every 5 seconds 
    let now = Date.now();
    let difference = now - this.lastTime;
    if(difference > 5000) {
      sLog(`Actor "${this.name}" Log - Difference: ${difference}`);
      this.lastTime = now;
    }

    // Determine if the actor died of a tragic accident
    if(randomNum(1,100000) < 5) {
      // Something tragic happened, that caused this actor to die
      this.die();
    } else {
      // I'm pretty sure that this is not the best way, but for some reason just
      // setTimeout(this.live, 1); does not work
      setTimeout(() => {this.live()}, 100);
    }
  }

  die() {
    // Log the death
    sLog(`Actor "${this.name}" died`);

    // This seems really a wrong way to do this, should not need to rely on an element outside of the scope - something else should do this, but how?
    delete actors[this.id];
  }
}

// Function to spawn a new actor
let spawnActor = () => {
  let id = 'a'+randomNum(1,9000000);
  sLog('Spawning an actor');
  let actorInstance = new Actor(id, id); // Rejoice!
  actorInstance.live();
  actors[id] = actorInstance;
}

// Simple loop that simulates the rendering of the screen
let lastTimeAnimated = null;
let mainAnimationLoop = () => {
  // Logs every 5 seconds to the log
  let now = Date.now();
  let difference = now - lastTimeAnimated;
  if(difference > 5000) {
    sLog(`Main Animation Loop Log - Difference: ${difference}`);
    lastTimeAnimated = now;
  }

  window.requestAnimationFrame(mainAnimationLoop);
}

// Simple loop that simulates a normal game main loop
let lastTime = null;
let mainLoop = () => {
  // Mainloop logs every 5 seconds to the log
  let now = Date.now();
  let difference = now - lastTime;
  if(difference > 5000) {
    sLog(`Main Loop Log - Difference: ${difference}`);
    lastTime = now;
  }

  // Random actor spawner
  if(randomNum(1,10000) < 5) {
    spawnActor(); // It truly is a blessed day!
  }

  setTimeout(mainLoop, 1);
}

// Should be obvious
let init = () => {
  mainAnimationLoop();
  mainLoop();
}

// Let's get started!
init();
</script>

Upvotes: 0

Views: 50

Answers (1)

Bergi
Bergi

Reputation: 664599

Basically I'd like to know if the right way to handle the "internal" loop would be using this, or if there is a better way of doing it (inside the live() function): setTimeout(() => {this.live()}, 100);

There are many other ways to do this (some of them even involving a real while loop), but none of them is "the one right way".

I'm pretty sure that this is not the best way, but for some reason just setTimeout(this.live, 1); does not work

See How to access the correct this / context inside a callback? for the why.

Second question would be to know the best way of destroying an instantiated class, withing the class, with something like "this.destroy()" - right now I am just removing the connection from the container to the object:

delete actors[this.id];

This seems really a wrong way to do this, should not need to rely on an element outside of the scope - something else should do this, but how?

You cannot "destroy" anything in javascript. If you want an instance to get garbage-collected, you need to remove all references to it. The right way to let an actor die is to just let it stop living - i.e. don't call .live() any more, and/or remove all timeouts that are scheduled to call it.

You don't need that container for anything (and in the code you've shown, you're not even using it). For some reason, spawnActor did store the instances, so it is its job to collect the dead. If you really don't need that collection, just omit it; if you use if for something then each actor should announce its death by setting an own property or by sending a message to the main actor, so that it can be removed from the collection as appropriate.

Upvotes: 1

Related Questions