【问题标题】:setTimeout inside iteration of iterationsetTimeout 在迭代的迭代内
【发布时间】:2015-08-25 08:01:08
【问题描述】:

我有下面的代码,我想在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.whilstiteration 但不是在每个async.forEachOfSeries 迭代之间。它根本不等待,而是继续循环,直到每个 async.forEachOfSeries 完成,然后调用 async.whilst setTimeout

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

【问题讨论】:

  • 请解释一下你有什么问题,为什么不能在async.forEachOfSeries里面设置?
  • @SharpEdge 它根本不等待,而是继续循环直到每个async.forEachOfSeries 完成,然后调用async.whilst setTimeout

标签: javascript node.js settimeout


【解决方案1】:

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

【讨论】:

  • @user1665355 第一问你明白我对setTimeout函数的解释了吗?
  • 好吧 setTimeout 似乎在 async.whilst 内部工作,然后它会延迟执行!
  • No man..setTimeout 不会延迟执行..它只是延迟显示在输出中的结果...您可能可以通过编写少量代码来测试...这是另一部分...现在您的主要问题..我的理解是您希望在向服务器发送另一个请求之前接收来自服务器的所有响应数据...对吗?
  • @Shanky setTimeout 确实会延迟执行。
  • @Shanky 好的!是的,我想在响应之前从服务器接收数据。
【解决方案2】:

我认为你并不完全理解 setTimeout 的工作原理:

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

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

然后,这些回调的执行按 FIFO 顺序进行。

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

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。

【讨论】:

  • 能否请您发布我的问题的解决方案? :)
  • 恐怕我不明白你真正想要做什么......代码有点乱,抱歉。例如,如果您不在该函数中使用 innerCallback 作为参数,为什么还要传递它?它似乎是一个函数本身,在另一个方法中调用:S
  • @innerCallbackasync.whilst 内部被调用。延迟每次迭代。
  • 为了这个解决方案的易用性,我给你 +1。
  • @Pablo 那么你的意思是我可以使用你的解决方案吗? Sharpedges 解决方案不起作用:/ 如果您能给我一些建议,我将不胜感激:)
【解决方案3】:

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

队列

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);
}

这远非最佳,因为无论如何你都陷入了所谓的末日金字塔

奖金

末日金字塔

当使用普通回调连续处理异步操作时,您最终会在彼此之间嵌套调用;这种嵌套会产生更多的缩进,形成一个金字塔(指向右侧),因此得名“末日金字塔”。

解决方案

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

【讨论】:

  • 只有一件事:循环将在不等待的情况下执行,只需将回调排队以在 5 秒后发生所有这些......你应该在每次调用 seTimeout 时再增加 5 秒
  • @Pablo Hm,你能不能编辑一下代码,这样更容易理解:)
  • 我们应该使用队列
  • @user1665355 看看我的回答,我在每个 setTimeout 调用中增加一秒
  • @Pablo 你在哪里增加一秒?我只看到setTimeout(executeWaterfall.bind(this), 5000);
【解决方案4】:

我对@9​​87654321@ 库并不十分熟悉,但在我看来,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,所有示例都未经测试。也许一切都大错特错,但这是我的直觉。

【讨论】:

    猜你喜欢
    • 2013-06-29
    • 2019-07-26
    • 2017-11-11
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 2016-02-17
    • 2020-11-01
    • 2012-10-04
    相关资源
    最近更新 更多