回调代码未执行

Callback code not executed

这是我的代码:

socket.on('add user', function (data) {
    d('"add user" event');
    d(data, true);

    debugger;

    UserEngine.login({
        username: data.username, 
        password: data.password
    }, function(err, user) {
        if (err) {
            d('Bad Login. Username: ' + data.username);
            return;
        }

        /*
        ** Code after this comment is never executed 
        */

        debugger;
        d('Login OK: ' + data.username);

        socket.username = data.username;

        usernames[data.username] = data.username;
        ++numUsers;
        addedUser = true;
        socket.emit('login', {
            numUsers: numUsers
        });

        socket.broadcast.emit('user joined', {
            username: socket.username,
            numUsers: numUsers 
        });

        return;
    });
});

如果 UserEngine.login(...) 中有错误,注释前的 If 语句将正常工作并且回调 returns。但如果该方法正常工作,则不会执行 If 语句之后的代码。 为什么?

编辑:

这是UserEngine模块的代码: http://pastebin.com/u2DQJrV3

我的猜测是您的代码在 login() 方法的某处抛出异常,并且由于大部分代码是在异步回调中执行的,您可能无法在控制台中获得任何异常信息。

这里有几个调试这个问题的建议:

  1. 您需要按照 login() 方法的确切流程。如果您在问题中所说的是真的,那么它有时不会调用回调,您需要找出原因。跟踪流程的最简单方法是在 login() 方法的每个分支中插入一个唯一标记的 console.log() 语句,这样您就可以准确地看到控制流的走向以及执行的内容和不执行的内容。一旦找到它,您就可以输出各种值以查看它为什么会这样或设置适当的断点并跟踪它。

  2. login() 方法或它调用的某些东西中也可能会抛出某种异常。如果您进入异步回调,异常可能不会记录在控制台中,因此事情可能会悄无声息地失败,并且可以跳过回调而不会在控制台中显示任何内容。如果您怀疑某个特定位置出现异常,您可以将自己的异常处理程序放在那里并记录异常是什么。请记住,每个异步作用域都需要自己的异常处理程序——您不能只在顶层使用一个异常处理程序。这是异步编码的一个复杂问题,也是为什么使用 promises 而不是普通回调更有用的原因之一,因为 promise 系统将为您捕获所有异步异常并将其转换为以异常为原因的被拒绝的 promise(因此异步异常不会不要默默地失败)。

  3. 如果执行 user.save() 代码分支,您的 login() 方法存在逻辑问题。我在下面记录了这个问题,虽然我不认为这是你的主要问题 - 但它是需要解决的问题。

  4. 我还建议您将日志记录放在调用 UserEngine.login() 的位置,以便记录所有可能的回调值。

建议的日志记录:

UserEngine.login({
    username: data.username, 
    password: data.password
}, function(err, user) {

    // =========== Add this ============
    console.log("UserEngine.login() callback");
    console.log(err, user);

    if (err) {
        d('Bad Login. Username: ' + data.username);
        return;
    }

login() 方法中的 user.save() 路径存在问题:

在您的 login() 代码中,作为 self._verifyToken() 函数调用的一部分,您需要更改此内容:

   login: function(args, callback) {
            /**
             * username [String]
             * password [String]
             */

            var self = this;

            if (!args.username || !args.password) {
                    return callback(Error.genObj(Error.code.MISSING_PARAMS));
            }

            User.findOne({
                    username: args.username
            }, function(err, user) {
                    console.log('[Debug] In User.findOne(...) Callback;');

                    if (err) {
                            return callback(Error.genObj(Error.code.INTERNAL));
                    }

                    if (!user) {
                            return callback(Error.genObj(Error.code.TOKEN_AUTH_FAILED));
                    }

                    if (self._generateHash({ password: args.password, salt: user.salt }) != user.password) {
                            return callback(Error.genObj(Error.code.TOKEN_AUTH_FAILED));
                    }

                    self._verifyToken({
                            token: user.token,
                            username: args.username,
                            key: Config.tokenKey
                    }, function(err) {
                            if (err) {
                                    var token = self._generateToken({ username: args.username, key: Config.key });
                                    user.token = token;

                                    user.save(function(err) {
                                            if (err) {
                                                    return callback(Error.genObj(Error.code.INTERNAL));
                                            }

                                            return callback(null, user);
                                    });
                            }

                            return callback(null, user);
                    });
            });
    },

为此:

   login: function(args, callback) {
            /**
             * username [String]
             * password [String]
             */

            var self = this;

            if (!args.username || !args.password) {
                    return callback(Error.genObj(Error.code.MISSING_PARAMS));
            }

            User.findOne({
                    username: args.username
            }, function(err, user) {
                    console.log('[Debug] In User.findOne(...) Callback;');

                    if (err) {
                            return callback(Error.genObj(Error.code.INTERNAL));
                    }

                    if (!user) {
                            return callback(Error.genObj(Error.code.TOKEN_AUTH_FAILED));
                    }

                    if (self._generateHash({ password: args.password, salt: user.salt }) != user.password) {
                            return callback(Error.genObj(Error.code.TOKEN_AUTH_FAILED));
                    }

                    self._verifyToken({
                            token: user.token,
                            username: args.username,
                            key: Config.tokenKey
                    }, function(err) {
                            if (err) {
                                    var token = self._generateToken({ username: args.username, key: Config.key });
                                    user.token = token;

                                    user.save(function(err) {
                                            if (err) {
                                                    return callback(Error.genObj(Error.code.INTERNAL));
                                            }

                                            return callback(null, user);
                                    });
                            } else {
                                // ======== put this in an else clause  ==========
                                return callback(null, user);
                            }
                    });
            });
    },

问题是,如果您从 verifyToken() 收到错误,那么它将开始 user.save(),但由于这是异步的,它将继续并在 return callback(null, user) 之前执行 [= =19=]操作完成,然后在user.save()完成时再次调用回调。