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

时间:2014-05-11 14:52:32

标签: 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){
            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 = rowsFromTheCallback。它似乎是该回调的局部变量。我确定这与变量范围和/或提升有关。只是想问你们是否有更好的方法来做到这一点,即使这个功能有效。看哈哈真是太难看了!

提前致谢

杰克

3 个答案:

答案 0 :(得分:1)

就范围而言,所有内部函数都应该能够读取和写入外部变量,除非它被内部变量声明或函数参数遮蔽。

您遇到的问题可能与代码的异步有关。看到这段代码:

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();

main将在setTimeouts被触发之前返回,因此它将返回该变量的原始值。为了等待内部回调运行,唯一的方法是让main在完成后对signa进行回调,而不是仅仅返回。

function main(onResult){
   delay(500, function(){
      //...
      onResult(allMembers);
   });

   // <-- no return value
});

main(function(allM){
    console.log(allM);
}); 

答案 1 :(得分:1)

请参阅异步库:https://github.com/caolan/async

async.series([
  getAllMembers,
  countMembers,
  ...
], function(err, results) {   
  // err contains an error if any of the functions fails. No more functions will be run.
  // results is an array containing results of each function if all the functions executed without errors
}));

function getAllMembers(callback) {
  connection.query('CALL CountMembers()', callback);
}

function countMembers(callback) {
 ...
}

如果函数的执行顺序无关紧要,可以使用async.parallel代替async.series。

答案 2 :(得分:0)

使用库来处理和封装&#34; Continuation Passing Style&#34; (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,例如,如果您有processFolderprocessFile的函数,您可以预期处理文件夹可能涉及处理其他文件夹和文件,并且该编号将会变化
  • 除了在没有参数的情况下调用next()并且结束当前分支
  • 之外什么都不做

装饰可以包括编写一个清理函数来清空nextList,将项目添加到nextList而不调用列表中的项目等。此时的替代方法是使用现有库这或继续写自己的。