Javascript:嵌套回调/ MySQL结果

时间:2010-11-30 05:28:18

标签: javascript scope serverside-javascript

我确信这是一个基本问题,但我一直在谷歌搜索一段时间,但找不到满意的答案..

我习惯用PHP编写MySQL选择查询,只需抓取结果,循环遍历每一行,然后在循环内根据每一行的列值进行进一步的查询。

但是,我现在正在使用javascript服务器端代码,它依赖于传递查询的SQL对象,然后是在运行查询后将调用的回调函数。

我对一些范围问题感到困惑,以及如何最好地干净利落。例如,我不想做类似的事情:

SQL.query("select * from blah", function(result) { 
  for(var i = 0; i < result.length; i++) {
    SQL.query("select * from blah2 where i =" + result[i].property, function(result2) {
      //now how do I access result from here? I know this isn't scoped correctly
    });
  }
});

编写这种嵌套SQL查询样式的标准方法是什么,没有范围问题/凌乱的代码?谢谢!

4 个答案:

答案 0 :(得分:0)

result 在第二个回调中可用,这就是JavaScript中的closures工作方式,这些函数可以访问其定义的外部作用域中的所有变量。

function outer() {
    var foo = 1;
    function inner() { // inherits the scope of outer
        var bla = 2;
        console.log(foo); // works!

        // another function in here well inherit both the scope of inner AND outer, and so on
    }
    inner();
    console.log(bla); // doesn't work, raises "ReferenceError: bla is not defined"
}
outer();

现在,在问题上,i将不会指向正确的值,它也将继承到第二个回调,但它是一个引用,因此将具有错误的值。

修复是创建另一个闭包:

SQL.query("select * from blah", function(result) { 
  for(var i = 0; i < result.length; i++) {
    (function(innerResult) { // anonymous function to provide yet another scope
        SQL.query("select * from blah2 where i =" + innerResult.property, function(result2) {
          // innerResult has the correct value
        });
    })(result[i]); // pass the current result into the function
  }
});

或额外的功能:

function resultThingy(result) {
   SQL.query("select * from blah2 where i =" + result.property, function(result2) {
       // result has the correct value
   });
}

SQL.query("select * from blah", function(result) { 
  for(var i = 0; i < result.length; i++) {
    resultThingy(result[i]);
  }
});

答案 1 :(得分:0)

这非常有趣......我从来没有听说过“服务器端javascript”......但是这可能有助于整理你的代码。我使用这种方法来组织我的ajax请求回调。

使用你的例子它看起来像这样。

SQL.query("select * from some_table", function(result){ runNestedQuery(result); });

function runNestedQuery(result){
  for(var i = 0; i < result.length; i++) {
    SQL.query("select * from blah2 where i =" + result[i].property, function(result2){ nestedResult(result2); });
  }
}

您的上述代码没有任何范围问题 - 但这是一种很好的方式,我喜欢组织这种事情。

答案 2 :(得分:0)

由于您使用的是服务器端Javascript,因此可以使用forEach。假设result instanceof Array == true

SQL.query("select * from blah", function(result) { 
  result.forEach(function(item, index) {
    SQL.query("select * from blah2 where i = " + item.property, function(result2) {
      console.log(item, index, result); //works as intended
    });
  });
});

如果result只是数组,那么

Array.prototype.forEach.call(result, function(item, index) { // etc...

应该这样做。

答案 3 :(得分:0)

正如其他人指出result实际上在嵌套回调中一直可用。

但是这有一个非常棘手的部分:

...因为嵌套查询是异步运行的,所以你的代码实际上会触发一堆的并行查询 - result中的每一行 - 都在同一个地方运行时间 (!)。这几乎可以肯定你想要的东西;除非result确实非常小,否则所有同时查询都会相当快地耗尽所有可用的数据库连接。

要解决此问题,您可以使用以下内容:

SQL.query("select * from blah", function(result) { 
    handleBlahRow( result, 0 );
});

function handleBlahRow( result, i ) {
    if( !result || (i >= result.length)) return;

    SQL.query("select * from blah2 where i =" + result[i].property, function(result2) {
        // kick off the next query
        handleBlahRow( result, i+1 );

        // result, i, *and* result2 are all accessible here.
        // do whatever you need to do with them
    });
});

以上将一次运行您的嵌套查询。如果你愿意,可以很容易地调整上面的内容以引入有限的并行性(例如,每次4次) - 虽然它可能没有必要。