所以,我正在尝试将我的聊天系统升级到Node.js,多年来我一直觉得不像这样的菜鸟!
在PHP中,它效率低下,但它绝对有意义。请求启动,它会计算出用户信息,房间信息,解析消息等等。相当线性。它在不同的地方调用了几个函数,但每次只需要直接向该函数发送所需的信息。当该函数完成后,它将返回,然后使用不同的信息完成更多工作。如果有异常,它(通常)会被捕获到可以提醒用户的级别。
据我所知,Node.js绝对不会像这样工作,而是主要由回调驱动 - 而且我有很多回调正在进行中。它必须处理初始连接,然后它必须检查cookie文件是否存在,然后它必须读取cookie文件,然后它必须从数据库获取一些用户信息,然后它必须得到一些来自数据库的其他用户信息,然后它必须从数据库中获取更多用户信息,然后它必须将用户添加到房间,如果有一段时间没有任何人 - 必须得到来自数据库的房间信息,然后最终响应请求。当我完成时,将至少有两个级别用于权限检查。
它与PHP进程没什么不同,但在PHP中它通过Apache进行多线程处理,因此请求可以坐在那里等待DB调用返回完全没有问题。用户查找,房间订阅,权限,所有这些都是单独处理的。
在Node.js中,“当你完成那个”系统时,太难以理解(我已经使用了客户端JS和jQuery),但变量传递当然是。其中很大一部分是try / catch被回调完全击败了。如果房间数据查询查询失败,该函数需要知道它应该将错误发送回哪个连接(此时可能是过去的两三个连接),因为它不会冒泡最多可以追上几个级别。因此,连接对象需要在整个过程中向下传递每个回调。这在处理异常时只是有点恶心,因为那些可能发生在任何地方,但当你到达其他变量必须一直传递到最后的一个回调线时,我的手指拒绝再打字,直到我看到那些非常糟糕的东西!
所以我想我想知道的是,如果有任何“黑客”我不熟悉可能允许变量“跳过”非嵌套回调。无限期地尝试/捕获链条也会很有效。
编辑:我无法轻视数百行代码,所以让我们看看我是否可以通过回调堆栈提供一些视觉帮助。同一行上的任何内容都是直接调用,下一行是回调。
connection.on('messaage') -> controller.validateUser -> fs.exists
fs.readFile
function() -> controller.addUser -> factory.user -> user.refreshData -> db.query
user.refreshChars -> db.query
user.refreshBlocks -> db.query
function() -> controller.addRoom -> factory.room -> room.refreshData -> db.query
room.getRole -> db.query
function() -> room.getUserList -> connection.sendUTF
如您所见,这些函数主要位于对象中,而不仅仅是嵌套的未命名函数,因为它们通常需要以任意顺序从多个位置访问。问题是,某些级别需要用户对象,而有些级别则不需要。如果try / catch工作正常,只有第一个和最后一个需要知道发送信息的连接。
我需要的是一种为这些不同功能提供不同信息的方法,而不必使用他们不需要的东西来淹没每个功能。 那是不受欢迎的做法。我还需要各种用户对象函数以非常不同的方式失败 - 对象不需要关注其自身的方式,因为它是调用函数的责任。
答案 0 :(得分:0)
initial(); // begins the process
// this starts things off
function initial() {
var props = { // this is the common object
onerror: function(err) {
if (err.msg === "reallyBadError")
return false; // false means stop
else
return true; // true means we can continue
},
someInitialData: {whatever:"data"}
};
doSomethingAsync(getFirstCallback(props));
}
function getFirstCallback(props) {
// return the actual callback function
return function(err, info) {
// if callback was passed an error, handle it
if (err && props.onerror(err) === false)
return;
props.info = info; // add something to props
doAnotherAsync(getSecondCallack(props));
};
}
function getSecondCallback(props) {
// return the actual callback function
return function(err, foo) {
// if callback was passed an error, handle it
if (err && props.onerror(err) === false)
return;
// maybe do something with props.info
props.foo = foo; // add something to props
doOneMoreAsync(getFinalCallack(props));
};
}
function getFinalCallback(props) {
// return the actual callback function
return function(err, bar) {
// if callback was passed an error, handle it
if (err && props.onerror(err) === false)
return;
// maybe do something with props.info and props.foo
// we also have access to the original props.whatever
};
}
这是一个原型版本:
var r = new Requester(); // begins the process
// Here's the implementation
function Requester() {
// "this" is the common object
this.someInitialData = {whatever:"data"};
doSomethingAsync(this.firstCallback.bind(this));
}
Requester.prototype.onerror: function(err) {
if (err.msg === "reallyBadError")
return false; // false means stop
else
return true; // true means we can continue
};
Requester.prototype.firstCallback = function(err, info) {
// if callback was passed an error, handle it
if (err && this.onerror(err) === false)
return;
this.info = info;
doAnotherAsync(this.secondCallack.bind(this));
};
Requester.prototype.secondCallback = function(err, foo) {
// if callback was passed an error, handle it
if (err && this.onerror(err) === false)
return;
this.foo = foo;
doOneMoreAsync(this.finalCallack.bind(this));
};
Requester.prototype.finalCallback = function(err, bar) {
// if callback was passed an error, handle it
if (err && this.onerror(err) === false)
return;
// The final code
};