我正在Node.Js中开发一个浏览器游戏,并且我有这个脚本:
game.js >>
var config = require('./game_config.js'); var mysql = require('mysql'); var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); var connexion = mysql.createConnection({ 'host': config.DB_HOST, 'user' : config.DB_USER, 'password' : config.DB_PASS, 'database' : config.DB_NAME }); var Player = require('./server/class.player.js'); io.on('connect', function(socket) { console.log('Co'); var player socket.on('login', function(data) { connexion.query("SELECT * FROM player WHERE nick = '"+data.login+"' AND pass = '"+data.pass+"'", function(err, rows) { if (err) { throw err; } else { if (rows.length == 0) { var dataRet = "LOG"; socket.emit('login', dataRet); } else { var p = rows[0]; var dataRet = new Player(p.id, p.nick, p.map_id, p.x, p.y, connexion).toJson(); console.log(dataRet); } // Without setTimeout it wouldn't work because the object didn't have the time to instantiate setTimeout(function() { socket.emit('login', dataRet); },1000); } }); }); socket.on('disconnect', function(socket) { console.log('Disco'); }); });
class.Player.js >>
var Player = function (id, name, map_id, x, y, connexion) { this.id = id; this.name = name; this.map_id = map_id ; this.x = x; this.y = y; this.link = connexion; this.toJson = function () { return { 'id' : this.id, 'name' : this.name, 'map_id' : this.map_id, 'x' : this.x, 'y' : this.y }; } } module.exports = User;
所以基本上,由于game.js中的“ setTimeout()”(对于socket.emit()事件),我的代码可以正常工作。如果我不使用它,由于Node.js的异步性,对象’dataRet’没有时间实例化,因此套接字发出“ undefined”或“ null”。
所以我在想,必须有一种侦听对象实例化的方法,以便在完成后立即通过socket.io发出它。
这本身与您的问题无关,但这很重要- 您有一个巨大的SQL注入漏洞,任何人都可以对数据库做任何事情。
代替:
connection.query( "SELECT * FROM player WHERE nick = '" + data.login + "' AND pass = '" + data.pass + "'", function (err, rows) { //... } );
可以使用:
connection.escape(data.login)和connection.escape(data.pass)到位的data.login和data.pass
connection.escape(data.login)
connection.escape(data.pass)
data.login
data.pass
要么:
connection.query( "SELECT * FROM player WHERE nick = ? AND pass = ?", [data.login, data.pass], function (err, rows) { // ... } );
它不仅更安全,而且实际上更容易阅读和理解。请参阅:转义查询的值在节点MySQL手册。
现在,回到您的问题。关于Player构造函数,没有什么异步的,因此您的问题必须是其他问题。这里让我们感到奇怪的是,Player.js导出User(未定义)而不导出(即定义),Player所以令我惊讶的是,它甚至完全可以工作。或者您可能发布了与实际使用的代码不同的代码,这可以解释为什么您的竞态条件在代码中并不明显。
User
Player
但是,如果您的Player构造函数正在进行一些异步调用,那么我建议添加一个回调参数并从构造函数中调用它:
var Player = function (id, name, map_id, x, y, connexion, callback) { this.id = id; this.name = name; this.map_id = map_id ; this.x = x; this.y = y; this.link = connexion; this.toJson = function () { return { 'id' : this.id, 'name' : this.name, 'map_id' : this.map_id, 'x' : this.x, 'y' : this.y }; } // some async call that you have to wait for // symbolized with setTimeout: setTimeout(function () { if (callback && typeof callback === 'function') { callback(this); } }, 1000); }
然后您可以将回调传递给构造函数,因此:
} else { var p = rows[0]; var dataRet = new Player(p.id, p.nick, p.map_id, p.x, p.y, connexion).toJson(); console.log(dataRet); } // Without setTimeout it wouldn't work because the object didn't have the time to instantiate setTimeout(function() { socket.emit('login', dataRet); },1000);
可以更改为:
} else { var p = rows[0]; var dataRet = new Player(p.id, p.nick, p.map_id, p.x, p.y, connexion, function () { socket.emit('login', dataRet); }).toJson(); console.log(dataRet); }
但是在这里,正如我所说的,没有什么是异步的,dataRet甚至在运行setTimeout之前就已经设置好了,所以这不能解决您的问题,但可以回答您的问题。
dataRet