【问题标题】:NodeJS Callback Scoping IssueNodeJS 回调范围问题
【发布时间】:2013-12-22 23:49:48
【问题描述】:

我对 Node.js 还是很陌生(这周刚开始),有一个基本部分我很难理解。我有一个辅助函数,它可以调用 MySQL 数据库来获取一些信息。然后,我使用回调函数将该数据返回给调用者,这可以正常工作,但是当我想在该回调之外使用该数据时,我遇到了麻烦。代码如下:

    /** Helper Function **/
    function getCompanyId(token, callback) {
            var query = db.query('SELECT * FROM companies WHERE token = ?', token, function(err, result) {
                var count = Object.keys(result).length;

                if(count == 0) {
                    return;
                } else {
                    callback(null, result[0].api_id);
                }
            });
        }

/*** Function which uses the data from the helper function ***/
    api.post('/alert', function(request, response) {
        var data = JSON.parse(request.body.data);
        var token = data.token;

        getCompanyId(token, function(err, result) {
            // this works
            console.log(result);
        });

        // the problem is that I need result here so that I can use it else where in this function.
    });

如您所见,只要我在回调范围内,我就可以访问 getCompanyId() 的返回值,但我需要在回调之外使用该值。我可以通过将所有逻辑粘贴在该回调中来在另一个函数中解决这个问题,但在这种情况下这不起作用。任何关于如何更好地构建它的见解将不胜感激。到目前为止,我真的很喜欢 Node.js,但显然我有很多需要学习。

【问题讨论】:

    标签: javascript node.js express callback


    【解决方案1】:

    简短的回答 - 你不能这样做而不违反 Node.js 的异步特性。

    想一想尝试在回调之外访问result 的后果 - 如果您需要使用该值,而回调尚未运行,您会怎么做?你不能sleep 等待值被设置——这与 Node 的单线程、事件驱动的设计不兼容。在等待回调运行时,您的整个程序必须停止执行。

    任何依赖于result 的代码都应该在getCompanyId 回调中:

    api.post('/alert', function(request, response) {
        var data = JSON.parse(request.body.data);
        var token = data.token;
    
        getCompanyId(token, function(err, result) {
            //Any logic that depends on result has to be nested in here
        });
    });
    

    学习 Node.js(异步编程是通用的)最难的部分之一是学习异步思考。一开始可能很困难,但值得坚持。您可以尝试按程序进行战斗和编码,但这将不可避免地导致无法维护、令人费解的代码。

    如果您不喜欢多个嵌套回调的想法,您可以查看 promises,它可以让您将方法链接在一起而不是嵌套它们。 This article 是对 Q 的一个很好的介绍,它是 Promise 的一种实现。

    【讨论】:

    • 我绝对可以做到,只是“感觉”很不对劲,至少和我习惯的不一样。感谢您的快速回复!
    • 可能感觉不对,但那是你内心的程序程序员所说的。适应异步可能需要一些时间,通常是因为它“感觉”不对。
    • +1。请参阅我的答案以获取可能有帮助的详细说明。
    【解决方案2】:

    如果您担心所有内容都塞在回调函数中,您可以随时命名该函数,将其移出,然后将该函数作为回调传递:

    getCompanyId(token, doSomethingAfter); // Pass the function in
    
    function doSomethingAfter(err, result) {
        // Code here
    }
    

    【讨论】:

    • 好的,那么通过这种方法,我如何将存储在“结果”中的值返回给调用函数?在 api.post('/alert' ....) 我会做: var result = getCompanyId;在 doSomethingAfter() 中我会返回结果吗?
    • 你不会,你必须在 doSomethingAfter 函数中做任何你需要的事情。它基本上只是一种防止大量嵌套函数的方法。你将无法做到var result = getCompany...
    【解决方案3】:

    如果你想避免回调气味,你需要像这样使用 Node 的事件发射器类:

    在文件顶部需要事件模块 -

    var emitter = require('events').EventEmitter();
    

    然后在你的回调中:

    api.post('/alert', function(request, response) {
        var data = JSON.parse(request.body.data);
        var token = data.token;
    
        getCompanyId(token, function(err, result) {
            // this works
            console.log(result);
            emitter.emit('company:id:returned', result);
        });
    
        // the problem is that I need result here so that I can use it else where in this function.
    });
    
    then after your function you can use the on method anywhere like so:
    
    getCompanyId(token, function(err, result) {
            // this works
            console.log(result);
            emitter.emit('company:id:returned', result);
        });
    
        // the problem is that I need result here so that I can use it else where in this function.
        emitter.on('company:id:returned', function(results) {
           // do what you need with results
        });
    

    请注意为您的事件设置良好的命名空间约定,这样您就不会弄乱事件,而且您应该注意您附加的侦听器的数量,这是一个很好的参考链接:

    http://www.sitepoint.com/nodejs-events-and-eventemitter/

    【讨论】:

    • 您尝试过github.com/kriskowal/qgithub.com/medikoo/deferred 吗?看起来他们做的事情大致相同。
    • 我查看了 q,我的和他们的区别在于他们使用的是 Promise,而我使用的是 pub/sub,这取决于你喜欢做什么,大多数情况下我使用 Promise,但有时在我看来,它们可能会有点冗长,而且更难维护,但我相信很多人会不同意
    【解决方案4】:

    当我开始将这些视为“一劳永逸”的方法时,我的“啊哈”时刻到来了。不要寻找从方法返回的返回值,因为它们不会返回。调用代码应该继续,或者只是结束。是的,感觉很奇怪。

    正如@joews 所说,您必须将取决于该值的所有内容放入回调中。

    这通常需要您传递一个额外的参数。例如,如果您正在执行典型的 HTTP 请求/响应,则计划沿着回调链每一步发送响应。最后的回调将(希望)在响应中设置数据,或设置错误代码,然后将其发送回用户。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多