2014-05-11 57 views
3

我在重新学习Javascript和上周的过程中编写这些代码为大学任务,我认为有可能是执行此代码的JavaScript Node.js的回调函数,变量的作用域问题

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。它似乎是这个回调的局部变量。我相信这与变量范围和/或提升有关。只是想问问你们,即使这个功能起作用,是否还有更好的方法来做到这一点。看哈哈是非常丑陋的!

预先感谢

杰克

+0

使他们的所有功能,而不是全部匿名呼叫! –

+0

代码的丑陋是节点回调的不幸副作用。我会建议看看涉及生成器的东西(yield关键字而不是回调),但不知道是否足够稳定以便在Node中使用。如果你想要更传统的东西,你可以看看像async.js这样的库,或者使用Promises – hugomg

+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将返回一个定时器,甚至解雇所以它会返回该变量的原始值之前。为了等待内部回调运行,唯一的方法是使main回调完成时signa,而不是仅仅返回。

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

    // <-- no return value 
}); 

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

谢谢,已经清除了我:) – user3621357

+1

当面对需要对数据库进行一系列调用,其中每个后续调用取决于前一次调用的返回时,此方法将导致性能不佳。更糟糕的是,如果所选的延迟时间不够长,则会失败。增加延迟,你只会使性能变差。 –

+0

@JasonAller:我希望延迟函数就像一些简单的异步函数一样,您可以使用它来测试没有数据库的东西并演示变量范围。是的,我不推荐使用实际的延迟来等待数据库响应(事实上,嵌套的数据库调用通常是一种气味 - 它总是值得研究,如果没有办法通过单个查询获得相同的数据) – hugomg

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。

0

使用库来处理和封装“Continuation Passing Style”(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而不需要从列表中调用项目等。此时的替代方法是为此使用现有库或继续编写自己的库。