我确信这是一个基本问题,但我一直在谷歌搜索一段时间,但找不到满意的答案..
我习惯用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查询样式的标准方法是什么,没有范围问题/凌乱的代码?谢谢!
答案 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次) - 虽然它可能没有必要。