【问题标题】:async for loop in node.jsnode.js中的异步for循环
【发布时间】:2014-02-06 16:35:42
【问题描述】:

我是这个 node.js 的新手..我对这个回调有点困惑..在我的应用程序中的 for 循环中,我正在调用异步函数调用,我认为我的问题是在我得到响应之前异步调用我的 for 循环被循环。

我的代码:

async.forEach(Object.keys(config), function(key, next) {
        search(config[key].query, function(err, result) { // 
        console.log("fffffffffff="+ util.inspect(result))-------- >>>Getting undefined..
            if (err) return next(err) // 
            var json = JSON.stringify({
                "result": result
            });
            results[key] = {
                "result": result
            }
            console.log("rrrrrrrr="+util.inspect(results[key]))
            next() // <---- critical piece.  This is how the forEach knows to continue to the next loop.  Must be called inside search's callback so that it doesn't loop prematurely.                   
        })
    },
    function(err) {
        console.log('iterating done');

         res.writeHead(200, {
        'content-type': 'application/json'
    });
    res.end(JSON.stringify(results));  
    });


}

搜索功能代码:

var matches = [];
    var qrySubString = query.substring(0, 4);
    client.query("select * from xxxxxxxxx where level4 ILIKE '%" + query + "%'", function(err, row1, fields) {
        for (var id in row1.rows) {                
            var match, name;                
            if (query == row1.rows[id].level4) {
                match = true;
                name = row1.rows[id].level4;
            }
            else {
                match = false;
                name = query;
            }
            matches.push({
                "id": id,
                "name": row1.rows[id].level4,
                "score": 100,
                "match": match,
                "type": [{
                    "id": "/people/presidents",
                    "name": "US President"
                }]
            })
        }           
        callback(matches);
    })

我想在成功执行1个搜索功能后执行for循环,我想我必须使用异步for循环。请指导我解决这个问题..提前谢谢..

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    您已正确诊断出您的问题,干得好。调用搜索代码后,for 循环就会继续运行。

    我是https://github.com/caolan/async 的忠实粉丝,它对我很有帮助。基本上有了它,你最终会得到类似的东西:

    var async = require('async')
    async.eachSeries(Object.keys(config), function (key, next){ 
      search(config[key].query, function(err, result) { // <----- I added an err here
        if (err) return next(err)  // <---- don't keep going if there was an error
    
        var json = JSON.stringify({
          "result": result
        });
        results[key] = {
          "result": result
        }
        next()    /* <---- critical piece.  This is how the forEach knows to continue to
                           the next loop.  Must be called inside search's callback so that
                           it doesn't loop prematurely.*/
      })
    }, function(err) {
      console.log('iterating done');
    }); 
    

    希望对你有帮助!

    【讨论】:

    • 我在尝试实现您的代码时遇到了一些问题..请查看我编辑的问题..
    • 您遇到的问题是我更改了您传递给search 的回调函数签名。我写的回调需要一个错误对象作为它的第一个参数,而不是结果对象。如果您要采用这种方法来执行此操作,则需要将您的 `callback(matches);` 调用更改为 `callback(err,matches);` 之类的调用。然后调用者可以查看错误对象。
    • 我没有在 async 中找到一个 forEach :S only each,它显然没有回调
    • 试试:async.eachSeries(arr, iterator, callback)
    • 你的样本最后错过了一个)
    【解决方案2】:

    我已将您的代码示例简化为以下几行,以便更容易理解对概念的解释。

    var results = [];
    var config = JSON.parse(queries);
    for (var key in config) {
        var query = config[key].query;
        search(query, function(result) {
            results.push(result);
        });
    }
    res.writeHead( ... );
    res.end(results);
    

    前面代码的问题是search函数是异步的,所以当循环结束时,还没有调用任何回调函数。因此,results 的列表为空。

    要解决此问题,您必须将代码放在回调函数中的循环之后。

        search(query, function(result) {
            results.push(result);
            // Put res.writeHead( ... ) and res.end(results) here
        });
    

    但是,由于回调函数被多次调用(每次迭代一次),您需要以某种方式知道所有回调都已被调用。为此,您需要统计回调的次数,并检查该次数是否等于异步函数调用的次数。

    要获取所有键的列表,请使用Object.keys。然后,为了遍历这个列表,我使用.forEach(您也可以使用for (var i = 0, key = keys[i]; i &lt; keys.length; ++i) { .. },但这可能会产生问题,请参阅JavaScript closure inside loops – simple practical example)。

    这是一个完整的例子:

    var results = [];
    var config = JSON.parse(queries);
    var onComplete = function() {
        res.writeHead( ... );
        res.end(results);
    };
    var keys = Object.keys(config);
    var tasksToGo = keys.length;
    if (tasksToGo === 0) {
       onComplete();
    } else {
        // There is at least one element, so the callback will be called.
        keys.forEach(function(key) {
            var query = config[key].query;
            search(query, function(result) {
                results.push(result);
                if (--tasksToGo === 0) {
                    // No tasks left, good to go
                    onComplete();
                }
            });
        });
    }
    

    注意:上例中的异步代码是并行执行的。如果需要按特定顺序调用函数,则可以使用递归来获得所需的效果:

    var results = [];
    var config = JSON.parse(queries);
    var keys = Object.keys(config);
    (function next(index) {
        if (index === keys.length) { // No items left
            res.writeHead( ... );
            res.end(results);
            return;
        }
        var key = keys[index];
        var query = config[key].query;
        search(query, function(result) {
            results.push(result);
            next(index + 1);
        });
    })(0);
    

    我展示的是概念,您可以在实现中使用众多(第三方)NodeJS 模块之一,例如async

    【讨论】:

    • @Subburaj 虽然我很欣赏 +1,但您应该根据其价值而不是发布它的用户的努力来评价答案。如果答案根本没有帮助,请不要投票。如果答案实际上是错误的,请投反对票。如果答案解决了您的问题,请接受。
    • 我遵循了您的第二个选项..它工作正常..您能否请我消除一些疑问:正如您所说,在执行 for 循环后没有执行回调,因此结果为空..现在 forEach调用回调的循环..
    • @Subburaj 在第一个示例中,当所有任务都处理完毕后,剩余任务数为零。然后,onComplete 被调用。
    • 嘿伙计,很好的解释。 1 个快速问题 - OnComplete 中的内容可以是回调函数调用吗?
    • @mles 为什么你认为回调会被调用得太早?想象一下,只有一把钥匙。然后tasksToGo = keys.length = 1。完成任务后,--tasksToGo == 0 为真,onComplete 被调用。如果有 2 个项目,则预增量运算符将给出 1,它不是 0,因此不会过早调用回调。
    【解决方案3】:

    我喜欢在这种情况下使用递归模式。例如,像这样:

    // If config is an array of queries
    var config = JSON.parse(queries.queryArray);   
    
    // Array of results
    var results;
    
    processQueries(config);
    
    function processQueries(queries) {
        var searchQuery;
    
        if (queries.length == 0) {
            // All queries complete
            res.writeHead(200, {'content-type': 'application/json'});
            res.end(JSON.stringify({results: results}));
            return;
        }
    
        searchQuery = queries.pop();
    
        search(searchQuery, function(result) {
            results.push(JSON.stringify({result: result}); 
            processQueries();            
        });
    }
    

    processQueries 是一个递归函数,它将从要处理的查询数组中拉出一个查询元素。然后当查询完成时回调函数再次调用processQueriesprocessQueries 知道在没有查询时结束。

    使用数组最容易做到这一点,但可以修改它以使用我想象的对象键/值。

    【讨论】:

      【解决方案4】:

      Node.js7.6 中引入了async await,这让Javascript 更漂亮。

      var results = [];
      var config = JSON.parse(queries);
      for (var key in config) {
        var query = config[key].query;
        results.push(await search(query));
      }
      res.writeHead( ... );
      res.end(results);
      

      为此,search 函数必须返回一个 promise 或者它必须是 async 函数

      如果它没有返回Promise,您可以帮助它返回Promise

      function asyncSearch(query) {
        return new Promise((resolve, reject) => {
         search(query,(result)=>{
          resolve(result);
         })
        })
      }
      

      然后将await search(query);这一行替换为await asyncSearch(query);

      【讨论】:

      • @PrashantTapase 你应该总是用 try catch 包裹 await 并承诺 reject 将转到 catch
      猜你喜欢
      • 2015-04-11
      • 2015-02-11
      • 2011-11-08
      • 2021-10-18
      • 1970-01-01
      • 2015-02-27
      • 1970-01-01
      • 1970-01-01
      • 2017-05-19
      相关资源
      最近更新 更多