0

PREFACE So it seems I've coded myself into a corner again. I've been teaching myself how to code for about 6 months now and have a really bad habit of starting over and changing projects so please, if my code reveals other bad practices let me know, but help me figure out how to effectively use promises/callbacks in this project first before my brain explodes. I currently want to delete it all and start over but I'm trying to break that habit. I did not anticipate the brain melting difficulty spike of asynchronicity (is that the word? spellcheck hates it)

The Project WorldBuilder - simply a CLI that will talk to a MySQL database on my machine and mimic basic text game features to allow building out an environment quickly in my spare time. It should allow me to MOVE [DIRECTION] LOOK at and CREATE [ rooms / objects ].

Currently it works like this: I use Inquirer to handle the prompt...

  request_command: function(){
    var command = {type:"input",name:"command",message:"CMD:>"}
    inquirer.prompt(command).then(answers=>{
      parser.parse(answers.command);
    }).catch((error)=>{
      console.log(error);
    });
  }`

The prompt passed whatever the user types to the parser

  parse: function(text) {
      player_verbs = [
        'look',
        'walk',
        'build',
        'test'
      ]
      words = text.split(' ');
      found_verb = this.find(player_verbs, words)[0];
      if (found_verb == undefined) {
        console.log('no verb found');
      } else {
        this.verbs.execute(found_verb, words)
      }
  }

The parser breaks the string into words and checks those words against possible verbs. Once it finds the verb in the command it accesses the verbs object... (ignore scope mouthwash, that is for another post)

verbs: {
    look: function(cmds) {
      // ToDo: Check for objects in the room that match a word in cmds
      player.look();
    },
    walk: function(cmds) {
      possible_directions = ['north','south','east','west'];
      direction = module.exports.find(possible_directions, cmds);
      player.walk(direction[0]);
    },
    build: function(cmds) {
      // NOTE scope_mouthwash exists because for some
      // reason I cannot access the global constant
      // from within this particular nested function
      // scope_mouthwash == menus
      const scope_mouthwash = require('./menus.js');
      scope_mouthwash.room_creation_menu();
    },
    execute: function(verb, args) {
      try{
        this[verb](args);
      }catch(e){
        throw new Error(e);
      }
    }
  }

Those verbs then access other objects and do various things but essentially it breaks down to querying the database an unknown number of times to either retrieve info about an object and make a calculation or store info about an object. Currently my database query function returns a promise.

  query: function (sql) {
    return new Promise(function(resolve, reject){
      db.query(sql, (err, rows)=>{
          if (err) {
            reject(err);
          }
          else {
            resolve(rows);
          }
      });
    });
  },

The Problem Unfortunately I jumped the gun and started using promises before I fully understood when to use them and I believe I should have used callbacks in some of these situations but I just don't quite get it yet. I solved 'callback hell' before I had to experience 'callback hell' and now am trying to avoid 'promise hell'. My prompt used to call itself in a loop after it triggered the required verbs but this whole approach broke down when I realized I'd get prompt messages in the middle of other prompt cycles for room building and such.

Should my queries be returning promises or should I rewrite them to use callback functions handled by whichever verb calls the query? How should I determine when to prompt the user again in the situation of having an unknown number of asynchronous processes?

So, put in a different way, my question is..

Given the parameters of my program how should I be visualizing and managing the asynchronous flow of commands, each of which may chain to an unknown number of database queries?

Possible Solution directions that have occurred to me..

Create an object that keeps track of when there are pending promises and simply prompts the user when all promises are resolved or failed.

Rewrite my program to use callback functions where possible and force a known number of promises. If I can get all verbs to work off callback functions I might be able to say something like Prompt.then(resolve verb).then(Prompt)...

Thank you for bearing with me, I know that was a long post. I know enough to get myself in trouble and Im pretty lost in the woods right now.

Jeremy Dixon
  • 101
  • 8
  • Your functions should always return something you want to know when they're done or fail: `player.walk(direction[0]);` could be: `return player.walk(direction[0]);` and `return this.verbs.execute(found_verb, words)` and then you can actually catch something in `request_command` if you were to do: `return parser.parse(answers.command);` Promises should be easier to maintain than callbacks, more on promises here: https://stackoverflow.com/a/47678417/1641941 – HMR Dec 08 '17 at 16:07
  • I don't mind taking a look at the full code if you can put it on github, butbucket or some git repo hosting site. – HMR Dec 08 '17 at 16:36
  • Thank you, I do have it on a repo, it's a mess right now. I'll get it pushed in a state that might make sense and let you know :P – Jeremy Dixon Dec 10 '17 at 18:47
  • https://bitbucket.org/Noxid86/worldbuilder/ – Jeremy Dixon Dec 10 '17 at 19:50
  • "Your functions should always return something you want to know when they're done or fail: player.walk(direction[0]);" Im not sure I follow you. Some functions dont return anything, they instead manipulate the database. I can rewrite them to return clearer info but at the end of the line a function needs to change the database and Im not sure, in that case, what returning anything would accomplish. – Jeremy Dixon Dec 10 '17 at 19:53
  • Please read the link I commented. If you do something asynchronous (like database interaction) then that function should return a promise or you can not know it failed. – HMR Dec 11 '17 at 03:56
  • Im sorry HMR I did read it, just not clicking in my head how it is applicable to my problem. Currently when i query a DB it does return a promise. In my script if I want to know the players location I use my query function which returns a promise. Then the function that is using that location to do something like determine if the player can move north and do so will use a .then() on the promise which was returned. Where im getting lost is that I have to do that a variable number of times between user commands. Did my bitbucket link work for you? Again thanks for the help. – Jeremy Dixon Dec 11 '17 at 13:40
  • Yes, the link works but I don't have a database so need to mock it out for testing. Didn't have time to look at it yet though. – HMR Dec 11 '17 at 14:36
  • I think I _may_ understand what you are getting at. If everything returns a promise I can hook it all up so that the command awaits a promise. So the main command loop would look something like **get command** .then(parse that shit and return a promise for the verb which drills down to whatever needs to do to the database).then(take a new command) and repeat. It may be that my initial instinct was correct but I just got lost and started second-guessing myself. But if everything returns a promise all the way back up to the command function I wont have to worry about how many promises are out. – Jeremy Dixon Dec 11 '17 at 14:36
  • If I am correct virtually everything in this program will be a promise because everything must be '.then-able'. So the parser which currently just goes and does stuff which creates promises at the database level needs to be rewritten to return a promise itself so that the main command loop can be **get_command().then(parse_command).then(get_command()** – Jeremy Dixon Dec 11 '17 at 14:48
  • Yes, let's say you have functions A,B,C and D called like `D( C ( B ( A())))` if A returns a promise and D needs the value after B and C then C and B needs to return a promise and D needs to wait for C to resolve. – HMR Dec 11 '17 at 16:20
  • Excellent thank you – Jeremy Dixon Dec 11 '17 at 22:17

0 Answers0