setTimeout inside iteration of iteration 迭代

setTimeout inside iteration of iteration

我有下面的代码,我想在 Myurl 的每次迭代之间放置一个 setTimeout。有多个classes,每个都包含多个元素。

//Some calculations before...
var i = 0;
async.whilst(
function () {
    return i <= thefooz.length - 1;
},
function (innerCallback) {

    //Some calculations where I get classes array.

    async.forEachOfSeries(classes, function (Myurl, m, eachDone) {
        // Here I want a delay
        async.waterfall([
            function (next) {
                connection.query(
                    'SELECT * FROM mydata WHERE UrlLink=? LIMIT 1', [Myurl],
                    next
                );
            },
            function (results, fields, next) {
                if (results.length !== 0) {
                    console.log("Already Present");
                    return next();
                }
                console.log("New Thing!");
                request(options2, function (err, resp, body) {
                    if (!err && resp.statusCode == 200) {
                        var $ = cheerio.load(body);
                        //Some calculations, where I get AllLinks.
                        var post = {
                            ThisUrl: AllLinks[0],
                            Time: AllLinks[1],
                        };
                        var query = connection.query('Insert INTO mydata Set ?', post, next);
                    };
                });
            }
        ], eachDone);

    }, function (err) {
        if (err) throw err;
    });
    setTimeout(function () {
        i++;
        innerCallback();
        console.log("Done");
    }, 20000);

    //Some calculations after...

那么我如何在 async.waterfall 中的每个 Myurl 之间设置延迟?假设我想要延迟 5 秒。我设法将 setTimeout 设置为 每个 async.whilst 迭代但不是每个 async.forEachOfSeries 迭代之间。它根本不等待,而是继续循环直到每个 async.forEachOfSeries 完成,然后调用 async.whilst setTimeout.

EDIT: 队列解决方案不起作用。该解决方案似乎只是转到下一页,下一页等等,而不输出到我的数据库。当然,我可以用错误的方式应用它,但我确实按照示例所说的去做了。

setTimeout 只会延迟结果显示在输出中...它不会延迟 setTimeout 方法内部或外部方法的执行...在后台线程将保持 运行 setTimeout 函数之后的代码...由于您使用的是异步,因此您倾向于使用 ajax 的 COMPLETE 方法,当您完成从服务器接收所有数据时将调用该方法

我认为您没有完全理解 setTimeout 的工作原理:

(function () {
    var seconds=0;
[1,2,3].forEach(function(value) {
    setTimeout(function() {
        console.log('Showing value '+value+ 'at '+Date());
    },1000*seconds++);
})
})()

此代码为每个元素创建一个回调函数,在一秒钟后执行。请注意 JS 是单线程的,所以代码真正做的是将 "executions" 添加到队列中。因此,如果当前执行没有停止,则不会调用回调。因此,您作为第二个参数传递给 setTimeout 函数的时间(以毫秒为单位)只是执行该代码的最短时间。

然后,按照先进先出的顺序执行这些回调。

更新:这是我正在解释的示例:

function myFunction() { 
    var test=0;
    setTimeout(function(){
        console.log("This is the current value of test: "+test); 
    }, 0);
    console.log("This is run first");
    for (var i=0;i<50;i++) {
        test++;
    }
    console.log("Main thread ending, not the callbacks will be executed");
} 

setTimeout在执行之前会等待0(零),但是由于主线程还没有结束,所以无法执行。然后,当循环结束时,执行回调,发现test是50,而不是0。

首先我们必须实现一个简单的队列

队列

function Queue() {
  var obj = {};  
  var queue = [];
  var _delay;
  
  function next() {
    // If queue is empty stops execution
    if(queue.length == 0) return;
    
    // Prepare next call to next
    setTimeout(next, _delay);
    // Take out an element from the queue and execute it.
    (queue.shift())();          
  }
  
  // Add a new function to the queue
  obj.add = function (myFunc) {
    queue.push(myFunc);
  };
  
  // Start the queue execution passing the delay between each call
  obj.run = function(delay) {
    _delay = delay;
    
    // call next function
    next();
  }
  
  return obj;
  
}

然后我们在代码里面使用

// create the queue
var myQueue = Queue();

async.forEachOfSeries(classes, function (Myurl, m, eachDone) {
  // Add the function to the queue
  myQueue.add(executeWaterfall.bind(this));
}, function (err) {
  if (err) throw err;
});

// Start the queue with 5 second delay
myQueue.run(5000);

function executeWaterfall() {
  async.waterfall([
    function (next) {
      connection.query(
        'SELECT * FROM mydata WHERE UrlLink=? LIMIT 1', [Myurl],
        next
      );
    },
    function (results, fields, next) {
      if (results.length !== 0) {
        console.log("Already Present");
        return next();
      }
      console.log("New Thing!");
      request(options2, function (err, resp, body) {
        if (!err && resp.statusCode == 200) {
          var $ = cheerio.load(body);
          //Some calculations, where I get AllLinks.
          var post = {
            ThisUrl: AllLinks[0],
            Time: AllLinks[1],
          };
          var query = connection.query('Insert INTO mydata Set ?', post, next);
        };
      });
    }
  ], eachDone);
}

这远非最佳,因为无论如何你都会掉进所谓的厄运金字塔

奖金

厄运金字塔

当用正常的回调连续处理异步操作时,你最终会在彼此之间嵌套调用;这种嵌套带来了更多的缩进,形成了一个金字塔(指向右边),因此得名“毁灭金字塔”。

解决方案

在这种情况下,最好使用一些 promise 模式来将您的代码从厄运金字塔中拯救出来,并促进此类问题的解决。

我不太熟悉 async 库,但对我来说 async.waterfall 会在每个 运行 之后调用 eachdone,因此 async.forEachOfSeries 知道它应该执行下一次迭代。假设 eachdone 是在没有参数的情况下调用的,我希望以下内容能够工作:

function executeWaterfall() {
  async.waterfall([
      ......
  ], function () { window.setTimeout(eachDone, 5000));
}

如果eachdone确实获取到参数,你也必须将它们发送过来。

作为替代方案,我希望您可以向瀑布添加另一个步骤,等待 5 秒。无论 eachdone 的参数如何,这都将起作用(但如果第三个瀑布函数需要更多参数,则可能会失败):

function executeWaterfall() {
  async.waterfall([
    function (next) {
      connection.query(
        'SELECT * FROM mydata WHERE UrlLink=? LIMIT 1', [Myurl],
        next
      );
    },
    function (results, fields, next) {
      if (results.length !== 0) {
        console.log("Already Present");
        return next();
      }
      console.log("New Thing!");
      request(options2, function (err, resp, body) {
        if (!err && resp.statusCode == 200) {
          var $ = cheerio.load(body);
          //Some calculations, where I get AllLinks.
          var post = {
            ThisUrl: AllLinks[0],
            Time: AllLinks[1],
          };
          var query = connection.query('Insert INTO mydata Set ?', post, next);
        };
      });
    },
    function (next) {
      window.setTimeout(next, 5000);
    }
  ], eachDone);
}

现在,我想再次强调,我对 async 并不熟悉,所有示例都未经测试。也许一切都大错特错,但这是我的直觉。