Javascript node.js回调函数变量范围问题

Javascript node.js回调函数变量范围问题,javascript,node.js,Javascript,Node.js,我正在重新学习Javascript,上周在为大学作业编写这段代码时,我认为可能有更好的方法来执行这段代码 app.get('/member/all', function(req, res) { connection.query('CALL GetAllMembers()', function(err,rows){ connection.query('CALL CountMembers()', function(err, allMembers){

我正在重新学习Javascript,上周在为大学作业编写这段代码时,我认为可能有更好的方法来执行这段代码

app.get('/member/all', function(req, res) {    
    connection.query('CALL GetAllMembers()', function(err,rows){
        connection.query('CALL CountMembers()', function(err, allMembers){
            console.log(err);
            connection.query('CALL CountAllIndMembers()', function(err,indMembers){
                console.log(err);
                connection.query('CALL CountInactiveMembers()', function(err,inactiveMembers){
                    console.log(err);
                    connection.query('CALL CountAllMembersInGroups()', function(err,groupMembers){
                        console.log(err);
                        res.render('members', {members : rows[0], title : "All Members",  groupMembers : groupMembers[0][0].AllGrpMembers,
                            inactiveMembers : inactiveMembers[0][0].AllInactiveMembers, indMembers : indMembers[0][0].AllIndMembers,
                            allMembers : allMembers[0][0].AllMembers, statistics : true});
                        });
                    });
                });
            });
        });
    });
});
当我试图在
app.get
下声明变量时,比如
var allMembers
。。。执行回调时,我无法从回调中设置
allMembers=rows
。它似乎是回调的一个局部变量。我确信这与可变范围和/或吊装有关。我只是想问你们是否有更好的方法来实现这一点,即使这个函数可以工作。这是非常丑陋的看哈哈

提前谢谢


Jack

就作用域而言,所有内部函数都应该能够读取和写入外部变量,除非它被内部变量声明或函数参数隐藏

您遇到的问题可能与代码的异步性有关。请参阅此代码:

function delay(n, cb){
    setTimeout(function(){ bs(delay) }, delay);
}

function main(){
    var allMembers = 17;
    delay(500, function(){
        console.log(allMembers);  // This looks at the outer "allMembers"
        allMembers = 18;

        delay(200, function(allMembers){  // <-- SHADOW
            console.log(allMembers); // This looks at the allMembers from "delay 200"'s callback
            allMembers = 42;
        });

        delay(300, function(){
            console.log(allMembers); //This is the outside "allMembers" again
        });
    });

    return allMembers; // Still 17!
}

main();
请参阅异步库:


如果函数的执行顺序无关紧要,则可以使用async.parallel而不是async.series。

使用库处理和封装与异步调用的“”(CPS)交互是有力量的。下面的代码不是来自库,但我将详细介绍它,并将其作为实现CPS的一种方法的示例

第一步是设置适用于范围的队列。本例使用了最简单的方法:

var nextList = [];
之后,我们需要一种方法来处理第一种情况,即需要对将来要执行的任务进行排队。在本例中,我专注于按顺序执行它们,因此我将其命名为
next

function next() {
    var todo,
        current,
        task,
        args = {};
    if (arguments.length > 0) { // if called with parameters process them
        // if parameters aren't in an array wrap them
        if (!Array.isArray(arguments['0'])) {
            todo = [arguments];
        } else { // we were passed an array
            todo = [];
            arguments['0'].forEach(function (item) {
                // for each item we were passed add it to todo
                todo.push(item);
            });
        }
        nextList = todo.concat(nextList);
        // append the new items to the end of our list
    }
    if (nextList.length > 0) { // if there are still things to do
        current = Array.prototype.slice.apply(nextList.shift());
        task = current[0];
        args = current.slice(1);
        task.apply(null, args); // execute the next item in the list
    }
}
这使我们可以拨打如下电话:

.map(function (filepath) {
    tasks.push(
        [
            handleAsset,
            {
                'path': filepath,
            }
        ]
    );
});
tasks.push([done]);
next(tasks);
这将按顺序为每个文件调用一次异步handleAsset。这将允许您使用代码,并将每个嵌套调用更改为以下形式的单独函数:

function memberAll() {
    app.get('/member/all', function(req, res) {
        if (err) {
            handleError(err, 'memberAll');
        } else {
            next(getAllMembers, 'parameters to that call if needed');
        }
    });
}
其中,
handleError
是一个常见的错误处理程序,下一个调用允许您将相关参数传递给所需的下一个函数。重要的是,在
if
语句的成功端,您可以:

  • 有条件地调用几个函数中的一个
  • 使用一系列调用调用调用next,例如,如果您有用于
    processFolder
    processFile
    的函数,则处理文件夹可能会涉及处理其他文件夹和文件,并且数量会有所不同
  • 除了调用无参数的
    next()
    并结束当前分支外,不执行任何操作

修饰可以包括编写一个清空
nextList
的干净函数,将项目添加到
nextList
,而不调用列表中的项目,在这一点上,另一种选择是使用现有库进行此操作,或者继续编写您自己的库。

使它们成为所有函数,而不是匿名调用它们!代码的丑陋是节点回调的一个不幸的副作用。我建议看一看涉及生成器的内容(yield关键字而不是回调),但不知道是否足够稳定,可以在节点中使用。如果您想要更传统的内容,可以查看诸如async.js之类的lib,或者使用PromisesReading了解“Contination传递样式”在这一点上,这将大大增加您的理解。感谢您为我澄清了这一点:)当需要对数据库进行一系列调用时,其中每个后续调用都取决于上一次调用的返回,此方法将导致性能不佳。更糟糕的是,如果选择的延迟时间不够长,它将失败。增加延迟只会使性能更差。@jasonaler:我打算将延迟函数作为一个简单的异步函数,您可以使用它在没有DB的情况下测试内容,并演示变量范围。是的,我不建议使用实际延迟来等待DB响应(事实上,嵌套的DB调用通常是一种气味——如果没有一种方法可以通过jsut单次查询获得相同的数据,这总是值得研究的)。很抱歉,我在第一次通读时错过了您的一些答案。当我通读你的答案时,我正专注于问题的另一个方面。
.map(function (filepath) {
    tasks.push(
        [
            handleAsset,
            {
                'path': filepath,
            }
        ]
    );
});
tasks.push([done]);
next(tasks);
function memberAll() {
    app.get('/member/all', function(req, res) {
        if (err) {
            handleError(err, 'memberAll');
        } else {
            next(getAllMembers, 'parameters to that call if needed');
        }
    });
}