Azamantes Azamantes - 6 months ago 47
Node.js Question

Node.js - function passed as argument suddenly becomes undefined

The following code is all inside Node.js v6.1.0.
My problem is that the

callback
suddenly becomes undefined for a reason unknown to me and I'd like to know what causes this behaviour. I use built-in Promise, not any library. This code was working before, when it was written all in 1 function with nested and chained callbacks inside
this.database.query(...).on(..., ...) etc.
.

The story goes like this:

// data.config is an object
// this.setPlayer.bind(this) <- this is the funtion that later becomes undefined all of a sudden
Game.createPlayer(data.config, this.setPlayer.bind(this));


What happens inside Game object:

class World {
// some other functions...

createPlayer(config, callback) {
const userID = config.user = parseInt(config.user);
const charID = config.id = parseInt(config.character);

console.log('Login: user #' + userID + ' as [' + charID + '].');
if(this.players[charID]){
console.log('Player#' + charID + ' already exists.'); // false
return null;
}
// -------------------------
// CALLBACK IS '[Function: bound setPlayer]' - OK
// -------------------------
console.log(callback);
this.getPlayerInfo(config, callback)
.then(this.getPlayerItems.bind(this));
}
getPlayerInfo(char, callback) {
// -------------------------
// CALLBACK STILL IS '[Function: bound setPlayer]' - OK
// -------------------------
console.log(callback);
return new Promise((PASS, FAIL) => {
this.database.query(SQL.character.info, [char.user, char.id]) // char.id === char.character
.on('result', row => {
char.name = row.charName;
char.location = this.locations[row.charLocation];
// -------------------------
// CALLBACK STILL IS '[Function: bound setPlayer]' - OK
// -------------------------
console.log(callback);
PASS(char, callback);
})
.on('end', FAIL);
});
}
getPlayerItems(char, callback) {
const slotsINV = {};
const slotsEQ = {};
let item;

// -------------------------
// !! HERE THE `callback` becomes undefined - ERROR
// -------------------------
console.log(callback);

this.database.query(SQL.character.items, [char.id])
.on('result', row => {
item = new Item({
id: ~~row.itemID,
name: row.itemName,
src: row.itemImage,
});
console.log('New item:', item);
switch(~~row.slotType) { // possibly more options later
case 1: {
slotsINV[~~row.itemSlot] = item;
break;
}
case 2: {
slotsEQ[row.itemSlotString] = item;
break;
}
}
}).on('error', error => {
console.log('Game.createPlayer [ERROR] ::', error.message);
}).on('end', () => {
char.inventory = new Inventory({ slots: slotsINV });
char.equipment = new Equipment({ slots: slotsEQ });
this.players[char.id] = new Character(char);
// -----------------------
// ERROR HERE: 'TypeError: callback is not a function'
// -----------------------
callback(this.players[char.id]);
});
}


I'm as much interested in why it becomes undefined as in how to preserve it.

The whole error message looks like this, if that's helpful:

C:\xampp\node_modules\mysql\lib\protocol\Parser.js:77
throw err; // Rethrow non-MySQL errors
^

TypeError: callback is not a function
at Query.database.query.on.on.on (C:\xampp\htdocs\GameEngine\engine\world.js:134:4)
at emitNone (events.js:91:20)
at Query.emit (events.js:185:7)
at Query.Sequence.end (C:\xampp\node_modules\mysql\lib\protocol\sequences\Sequence.js:99:12)
at Query._handleFinalResultPacket (C:\xampp\node_modules\mysql\lib\protocol\sequences\Query.js:1
44:8)
at Query.EofPacket (C:\xampp\node_modules\mysql\lib\protocol\sequences\Query.js:128:8)
at Protocol._parsePacket (C:\xampp\node_modules\mysql\lib\protocol\Protocol.js:280:23)
at Parser.write (C:\xampp\node_modules\mysql\lib\protocol\Parser.js:73:12)
at Protocol.write (C:\xampp\node_modules\mysql\lib\protocol\Protocol.js:39:16)
at Socket.<anonymous> (C:\xampp\node_modules\mysql\lib\Connection.js:96:28)


The
C:\xampp\htdocs\GameEngine\engine\world.js:134:4
line is exactly the line where
callback
is called:

char.inventory = new Inventory({ slots: slotsINV });
char.equipment = new Equipment({ slots: slotsEQ });
this.players[char.id] = new Character(char);
callback(this.players[char.id]); // <- HERE

Answer

TL;DR

PASS (or resolve as it is normally called) only accepts 1 argument. Try passing it an array if you want to return multiple things as your result.

Full answer

This is the culprit:

PASS(char, callback);

Your PASS (or resolve function as it's typically called) only accepts 1 argument, and you're trying to pass it 2 arguments. The first argument char will be defined as your result, but callback won't.

Try this:

// inside of your `createPlayer` method
PASS([char, callback]);

// ...later

getPlayerItems(result) {
  const char = result[0];
  const callback = result[1];

  // the rest of your code
}

The fix proposed there is to pass an array of your char and callback since PASS only accepts one argument.

I've duplicated your error with a minimal example in this jsbin:

function doSomething() {
  return new Promise(function(resolve, reject) {
    resolve(1, 2);
  });
}

doSomething().then(function(a, b) {
  console.log(a, b); // 1, undefined
});

This demonstrates that resolve (or PASS as you're calling it) only accepts on argument.

And then here's a jsbin with the proposed fix:

function doSomething() {
  return new Promise(function(resolve, reject) {
    resolve([1, 2]);
  });
}

doSomething().then(function(result) {
  console.log(result[0], result[1]); // 1, 2
});

Also, I did a bit of searching on StackOverflow, and there's already an answer to this problem:

just the first parameter will be treated as resolution value in the promise constructor.

Comments