Home > OS >  Using promises to change properties, but property is not updating [JavaScript]
Using promises to change properties, but property is not updating [JavaScript]

Time:02-27

Description

I am writing a Discord Bot in NodeJS and am currently experiencing a very odd issue.

What I am wanting to do is to get the result of health via method getHP(), afterwards updating the health property with the setHP() method.

This works for one class, but not for another. So basically, the code is practically the same, but for the other class it does not update the property.

I am calling both the classes their setHP() methods in their constructors.

Code:


// Player.js - This works and displays: { current: 98, max: 98 }

class Player {
  constructor(member, msg) {
    this.member = member
    this.msg = msg
    this.setHP()
  }

  health = {}
  setHP() {
    this.getHP.then(hp => {
      this.health = { current: hp.current, max: hp.current }
    })
  }

  get getHP() {
    return new Promise(async (resolve) => {

      const stats = await this.stats
      resolve(stats.find(stat => stat.id === 'health'))
    })
  } 
  
  get stats() {
    return new Promise(async (resolve) => {
      const result = await DB.query(`select stats from members where member_id = ${this.member.id} `)
      resolve(JSON.parse(result[0][0].stats))
    })
  }

  get difficulty() {
    return new Promise(async (resolve) => {
      const result = await DB.query(`select difficulty from members where member_id = ${this.member.id} `)
      resolve(result[0][0].difficulty)
    })
  }
}

// Enemy.js - Doesn't work and displays: {}

class Enemy {
  constructor(player) {
    this.player = player
    this.setHP()
  }

  hp = {}
  setHP() {
    this.getHP.then(int => {
      this.hp = { current: int, max: int }
    })
  }

  get getHP() {
    return new Promise(async (resolve) => {
      const difficulty = await this.player.difficulty
      const int = Math.floor(this.player.health.current * (difficulty * (Math.random() * 0.10   0.95)))
      resolve(int)
    })
  }

// minion_fight.js - Where the classes are used

const Enemy = require("Enemy.js")
const Player = require("Player.js")

module.exports.execute = async (msg) => {
  const player = new Player(msg.member, msg)
  const enemy = new Enemy(player)

  // ...
}

CodePudding user response:

The main issue is that the player instance has a pending promise that will eventually resolve and set the player's health property. But before that happens, the enemy instance is created, and it accesses the above mentioned health property from the given player before it has been set. So this.player.health.current cannot be evaluated.

It is better to:

  • Avoid launching asynchronous tasks in a constructor. Instead create methods that do this.
  • Avoid creating promises with new Promise, when there is already a promise to await. This is an anti-pattern.
  • Don't use getters/setters for asynchronous tasks. Just make them asynchronous methods -- it will make the code easier to understand.
  • Please terminate your statements with a semi-colon. You don't really want to make the interpretation of your code dependent on the automatic semi-colon insertion algorithm.

Here is the suggested correction -- but I didn't test it, so I hope you'll at least get the gist of the proposed changes:

// Player.js

class Player {
  constructor(member, msg) {
    this.member = member;
    this.msg = msg;
  }

  health = {}

  async setHP() {
    const hp = await this.getHP();
    this.health = { current: hp.current, max: hp.current };
    return this.health;
  }

  async getHP() {
    const stats = await this.stats();
    return stats.find(stat => stat.id === 'health');
  } 
  
  async stats() {
    const result = await DB.query(`select stats from members where member_id = ${this.member.id} `);
    return JSON.parse(result[0][0].stats);
  }

  async difficulty() {
    const result = await DB.query(`select difficulty from members where member_id = ${this.member.id} `);
    return result[0][0].difficulty;
  }
}

// Enemy.js

class Enemy {
  constructor(player) {
    this.player = player;
  }

  hp = {}
  
  async setHP() {
    const current = await this.getHP();
    this.hp = { current, max: int };
    return this.hp;
  }

  async getHP() {
    const playerHealth = await this.player.getHP(); // To be sure the promise is resolved!
    const difficulty = await this.player.difficulty();
    return Math.floor(playerHealth.current * (difficulty * (Math.random() * 0.10   0.95)));
  }
}

// minion_fight.js

const Enemy = require("Enemy.js")
const Player = require("Player.js")

module.exports.execute = async (msg) => {
  const player = new Player(msg.member, msg);
  const enemy = new Enemy(player);
  await enemy.setHP();
  // ...
}
  • Related