延迟节点js中的每个循环迭代,异步

时间:2015-05-28 18:37:30

标签: javascript node.js for-loop

我有以下代码:

var request = require('request');
var cheerio = require ("cheerio");
var async= require("async");

var MyLink="www.mylink.com";

    async.series([

        function(callback){
            request(Mylink, function (error, response, body) {
                if (error) return callback(error); 
                var $ = cheerio.load(body);
                //Some calculations where I get NewUrl variable...
                TheUrl=NewUrl;
                callback();
            });
        },
        function(callback){
            for (var i = 0; i <=TheUrl.length-1; i++) {
                var url = 'www.myurl.com='+TheUrl[i];
                request(url, function(error, resp, body) { 
                    if (error) return callback(error); 
                    var $ = cheerio.load(body);
                    //Some calculations again...
                    callback();
                });
            };
        }
      ], function(error){
        if (error) return next(error);
    });

是否有人建议如何延迟for loop中的每个循环迭代?比如说,代码在每次迭代完成后等待10秒。我尝试了setTimeout,但没有设法解决这个问题。

5 个答案:

答案 0 :(得分:15)

您可以按以下间隔设置执行代码的超时时间,如下所示:

var interval = 10 * 1000; // 10 seconds;

for (var i = 0; i <=TheUrl.length-1; i++) {
    setTimeout( function (i) {
        var url = 'www.myurl.com='+TheUrl[i];
        request(url, function(error, resp, body) { 
            if (error) return callback(error); 
            var $ = cheerio.load(body);
            //Some calculations again...
            callback();
        });
    }, interval * i, i);
}

所以第一个立即运行(间隔* 0为0),第二个运行十秒后运行等等。

您需要在i中发送setTimeout()作为最终参数,以便将其值绑定到函数参数。否则,访问数组值的尝试将超出范围,您将获得undefined

答案 1 :(得分:15)

另一种选择是使用async.eachSeries。例如:

async.eachSeries(TheUrl, function (eachUrl, done) {
    setTimeout(function () {
        var url = 'www.myurl.com='+eachUrl;
        request(url, function(error, resp, body) { 
            if (error) return callback(error); 
            var $ = cheerio.load(body);
            //Some calculations again...
            done();
        });
    }, 10000);
}, function (err) {
    if (!err) callback();
});

答案 2 :(得分:11)

使用async/await

延迟多页抓取

我是异步库的忠实粉丝,而且我已经使用了很长时间。但是,现在有async/await。您的代码变得更容易阅读。例如,这将是您的主要功能:

const urls = await fetchUrls(INITIAL_URL);

for (const url of urls) {
    await sleep(10000);
    const $ = await fetchPage(url);
    // do stuff with cheerio-processed page
}

好多了,不是吗?在我详细了解fetchPage()fetchUrls()的工作原理之前,让我们首先回答您在获取下一页之前如何等待的问题。睡眠功能非常简单:

async function sleep(millis) {
    return new Promise(resolve => setTimeout(resolve, millis));
}

您可以在我的其他答案here中获得有关其工作原理的完整说明。

好的,回到其他功能。 request库具有启用许可的版本,可以与async/await一起使用。我们来看看fetchPage()实施的方式:

async function fetchPage(url) {
    return await request({
        url: url,
        transform: (body) => cheerio.load(body)
    });
}

由于request正在返回承诺,我们可以await。我也抓住机会使用transform属性,它允许我们在解决promise之前转换响应主体。我将它传递给Cheerio,就像你在代码中所做的一样。

最后,fetchUrls()可以调用fetchPage()并处理它以获取您的URL数组,然后再解析其承诺。这是完整的代码:

const
    request = require("request-promise-native"),
    cheerio = require("cheerio");

const
    INITIAL_URL = "http://your-initial-url.com";

/**
 * Asynchronously fetches the page referred to by `url`.
 *
 * @param {String} url - the URL of the page to be fetched
 * @return {Promise} promise to a cheerio-processed page
 */
async function fetchPage(url) {
    return await request({
        url: url,
        transform: (body) => cheerio.load(body)
    });
}

/**
 * Your initial fetch which will bring the list of URLs your looking for.
 *
 * @param {String} initialUrl - the initial URL
 * @return {Promise<string[]>} an array of URL strings
 */
async function fetchUrls(initialUrl) {
    const $ = await fetchPage(initialUrl);
    // process $ here and get urls
    return ["http://foo.com", "http://bar.com"];
}

/**
 * Clever way to do asynchronous sleep. 
 * Check this: https://stackoverflow.com/a/46720712/778272
 *
 * @param {Number} millis - how long to sleep in milliseconds
 * @return {Promise<void>}
 */
async function sleep(millis) {
    return new Promise(resolve => setTimeout(resolve, millis));
}

async function run() {
    const urls = await fetchUrls(INITIAL_URL);
    for (const url of urls) {
        await sleep(10000);
        const $ = await fetchPage(url);
        // do stuff with cheerio-processed page
    }
}

run();

要将request与promises一起使用,请按以下方式安装:

npm install request
npm install request-promise-native

然后在代码中require("request-promise-native"),就像上面的示例一样。

答案 3 :(得分:9)

由于您已经在使用asyncasync.wilst可以很好地替代for

whilst是一个异步while的函数。每次迭代仅在前一次迭代调用其完成回调之后运行。在这种情况下,我们可以使用setTimeout将完成回调的执行推迟10秒。

var i = 0;
async.whilst(
    // test to perform next iteration
    function() { return i <= TheUrl.length-1; },

    // iterated function
    // call `innerCallback` when the iteration is done
    function(innerCallback) {
        var url = 'www.myurl.com='+TheUrl[i];
        request(url, function(error, resp, body) { 
            if (error) return innerCallback(error); 
            var $ = cheerio.load(body);
            //Some calculations again...

            // wait 10 secs to run the next iteration
            setTimeout(function() { i++; innerCallback(); }, 10000);
        });
    },

    // when all iterations are done, call `callback`
    callback
);

答案 4 :(得分:0)

这是在 for 循环中提供延迟的示例代码。

const sleep = (milliseconds) => {
    const date = Date.now();
    let currentDate = null;
    do {
      currentDate = Date.now();
    } while (currentDate - date < milliseconds);
};

for (let index = 0; index < 10; index++) {
    console.log(index);
    sleep(1000);
}