繁体   English   中英

Node.js 同步循环或迭代异步语句

[英]Node.js synchronously loop or iterate over asynchronous statements

我想为每个循环做一个但让它同步运行。 循环的每次迭代都会执行一次 http.get 调用,并返回 json 以将值插入到数据库中。 问题是 for 循环异步运行,这导致所有 http.gets 都同时运行,我的数据库最终没有插入所有数据。我正在使用 async-foreach 尝试做我想做的事它可以做,但如果我能以正确的方式做,我就不必使用它。

mCardImport = require('m_cardImport.js');
var http = require('http');
app.get('/path/hi', function(req, res) {

mCardImport.getList(function(sets) {
  forEach(sets, function(item, index, arr) {
    theUrl = 'http://' + sets.set_code + '.json';
    http.get(theUrl, function(res) {

      var jsonData = '';
      res.on('data', function(chunk) {
        jsonData += chunk;
      });

      res.on('end', function() {
        var theResponse = JSON.parse(jsonData);
        mCardImport.importResponse(theResponse.list, theResponse.code, function(theSet) {
          console.log("SET: " + theSet);
        });
      });
    });
  });
});
});

还有我的模特

exports.importResponse = function(cardList, setCode, callback) {

mysqlLib.getConnection(function(err, connection) {

forEach(cardList, function(item, index, arr) {

  var theSql = "INSERT INTO table (name, code, multid, collector_set_num) VALUES "
   + "(?, ?, ?, ?) ON DUPLICATE KEY UPDATE id=id";
  connection.query(theSql, [item.name, setCode, item.multid, item.number], function(err, results) {
    if (err) {
      console.log(err);
    };
  });
});
});
callback(setCode);
};

使用递归,代码非常干净。 等待 http 响应返回,然后启动下一次尝试。 这将适用于所有版本的节点。

var urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];

var processItems = function(x){
  if( x < urls.length ) {
    http.get(urls[x], function(res) {

      // add some code here to process the response

      processItems(x+1);
    });
  }
};

processItems(0);

使用 Promise 的解决方案也可以很好地工作,并且更加简洁。 例如,如果您有一个返回 promise和 Node v7.6+的 get 版本,您可以编写一个像这个例子一样的 async/await 函数,它使用了一些新的 JS 特性。

const urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];

async function processItems(urls){
  for(const url of urls) {
    const response = await promisifiedHttpGet(url);    
    // add some code here to process the response.
  }
};

processItems(urls);

注意:这两个示例都跳过了错误处理,但您可能应该在生产应用程序中使用它。

要循环和同步链接异步操作,最干净的解决方案可能是使用 Promise 库(ES6 中引入了 Promise,这是要走的路)。

使用Bluebird ,这可能是

Var p = Promise.resolve();
forEach(sets, function(item, index, arr) {
    p.then(new Promise(function(resolve, reject) {
         http.get(theUrl, function(res) {
         ....
         res.on('end', function() {
              ...
              resolve();
         }
    }));
});
p.then(function(){
   // all tasks launched in the loop are finished
});

我发现每次调用完成后我都没有释放我的 mysql 连接,这绑定了连接导致它失败并且似乎是同步问题。

在显式调用connection.release();之后connection.release(); 它使我的代码即使以异步方式也能 100% 正确工作。

感谢发布此问题的人。

"use strict";

var Promise = require("bluebird");
var some = require('promise-sequence/lib/some');

var pinger = function(wht) {
    return new Promise(function(resolve, reject) {
        setTimeout(function () { 

            console.log('I`ll Be Waiting: ' + wht);
            resolve(wht);

        }, Math.random() * (2000 - 1500) + 1500);
    });
}

var result = [];
for (var i = 0; i <= 12; i++) {
    result.push(i);
}

some(result, pinger).then(function(result){
  console.log(result);
});

只需将循环包装在async函数中即可。 这个例子说明了我的意思:

const oneSecond = async () => 
    new Promise((res, _) => setTimeout(res, 1000));

此函数仅在 1 秒后完成:

const syncFun = () => {
    for (let i = 0; i < 5; i++) {
        oneSecond().then(() => console.log(`${i}`));
    }
}

syncFun(); // Completes after 1 second ❌

这个按预期工作,在 5 秒后完成:

const asyncFun = async () => {
    for (let i = 0; i < 5; i++) {
        await oneSecond();
        console.log(`${i}`);
    }
}

asyncFun(); // Completes after 5 seconds ✅
var urls = ['http://stackoverflow.com/', 'http://security.stackexchange.com/', 'http://unix.stackexchange.com/'];

for (i = 0; i < urls.length; i++){
   http.get(urls[i], function(res) {
       // add some code here to process the response
   });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM