1

I am trying to implement a CLI version of the Rock Paper Scissors game. I am using the inquirer module to handle IO. My main function looks like this:

RockPaperScissors.prototype.gameLoop = function()
{
var x;
var Promise = require('bluebird');


//simple promise test
//this.playGame().then(function(){ console.log("The end");});

Promise.coroutine(function*()
{

    //for(x=0;x<this.maxTurns;x++)
    //{
        console.log('Printing '+ x.toString());
        var action = yield this.playGame();
    //}    

    if(this.playerScore > this.serverScore) { console.log('Player wins match');} else {console.log('Server wins match');  }    

});
};

exports.RockPaperScissors = RockPaperScissors;

The playGame() function returns a promise made by using new Promise(). If I do this:

this.playGame().then(function(){ console.log("The end");});

The promise executes correctly. However, when used inside Promise.coroutine(), nothing is executed. What am I missing here?

This is the code for the playGame() function:

RockPaperScissors.prototype.playGame = function()
{

    var inq = require('inquirer');  
    var rand = require('random-js');
    var _ = require('lodash');
    var promise = require('bluebird');

    //make possibilities local
    var possibilities = this.possibilities;

    console.log ('------------------ Stats ----------------');
    console.log ('Player: ' +this.playerScore+'  Server: '+this.serverScore);
    console.log ('-----------------------------------------');

    var question1 ={
        type:'rawlist',
        name:'option',
        message:'Please choose Rock, paper or scissors:',
        choices:['Rock','Paper','Scissors']
    };

    return new promise(function(resolve,reject)
    {
       inq.prompt([question1],function(answers)
       {
            console.log('You chose '+answers.option);
            var playerObject = answers.option;
            //random with Mersenne Twister API
            var r = new rand(rand.engines.mt19937().autoSeed());
            var myPlay =r.integer(0,2);
            var serverObject ='';
            switch(myPlay)
            {
                case 0:
                    serverObject='Rock';
                    break;
                case 1:
                    serverObject ='Paper';
                    break;
                case 2:
                    serverObject='Scissors';
                    break;
            }

            var result='', action='';
            //choose winner by using a lodash function!
            _.forEach(possibilities,function(e){
                if (e[0]==serverObject && e[1] ==playerObject) 
                {
                     result=e[2];
                     action=e[3];
                }
            });

            console.log('I chose ' + serverObject+ "\n")
            console.log (result);

            if (action=='win') {this.playerScore++;}
            if (action=='lose'){this.serverScore++;}
            resolve(action);
        });
    });
};
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
tutiplain
  • 1,427
  • 4
  • 19
  • 37
  • Try to [promisify at the lowest level only](http://stackoverflow.com/q/22519784/1048572) (which in your case would be only the `prompt` method). Put everything else in a `.then` callback. – Bergi Nov 14 '15 at 21:42

2 Answers2

3

Promise.coroutine is a higher-order function, i.e. it takes a generator function and returns another function that, when called, will return the promise you're looking for. As @robertklep said, you're not even calling that returned function.

Instead, you are however supposed to wrap the complete method in Promise.coroutine, instead of calling it within the method. Your code should look like this:

var Promise = require('bluebird');

RockPaperScissors.prototype.gameLoop = Promise.coroutine(function*() {
    // simple promise test:
    // yield this.playGame();
    // console.log("The end");

    for (var x=0;x<this.maxTurns;x++) {
        console.log('Printing '+ x.toString());
        var action = yield this.playGame();
    }

    if (this.playerScore > this.serverScore) {
        console.log('Player wins match');
    } else {
        console.log('Server wins match');
    }
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • HI, this looks much cleaner than my approach. Why does this preserve the value of "this"? I'm just trying to understand why sometimes it has a different object than one would expect. – tutiplain Nov 16 '15 at 10:12
  • Because the function created by `Promise.coroutine` does call the generator function with its `this` value. – Bergi Nov 16 '15 at 15:15
1

Promise.coroutine() "returns a function that can use yield to yield promises" (doc), but you're not actually calling that function.

You probably want something like this:

Promise.coroutine(function*() {
...
})();
//^^ calling it
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • I had to make some extra modifications in order to get it to work. For example, I had to save a reference to the "this" object and pass it in as an argument to the generator function, as it seems the this context gets lost (bound to a different value) when inside promises. I also had to to this to the playGame() function. I still don't know why promises do that. – tutiplain Nov 14 '15 at 20:14
  • @tutiplain `bluebird` has a [`bind`](http://bluebirdjs.com/docs/api/promise.bind.html) method that you can use to set the `this` for a function. So something like this: `Promise.bind(this).coroutine(function*() { ... })`. – robertklep Nov 14 '15 at 20:47
  • @robertklep: Rather `Promise.coroutine(function*() {…}).call(this)`. But have a look at my answer for the better solution which also takes care of the `this` issue. – Bergi Nov 14 '15 at 21:48