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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30514584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:18:32  来源:igfitidea点击:

Delay each loop iteration in node js, async

javascriptnode.jsfor-loop

提问by user1665355

I have the code below:

我有下面的代码:

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

Does anyone have a suggestion about how I can delay each loop iteration in the for loop? Say, the code waits 10 seconds after each iteration is complete. I tried setTimeoutbut didn't manage that to work.

有没有人对我如何延迟每个循环迭代有建议for loop?比如说,代码在每次迭代完成后等待 10 秒。我试过了,setTimeout但没有成功。

回答by Trott

You can set a timeout for the execution of the code at increasing intervals like this:

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

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

So the first one runs right away (interval * 0 is 0), second one runs after ten seconds, etc.

所以第一个立即运行(间隔 * 0 为 0),第二个在十秒后运行,依此类推。

You need to send ias the final parameter in the setTimeout()so that its value is bound to the function argument. Otherwise the attempt to access the array value will be out of bounds and you will get undefined.

您需要将其i作为最后一个参数发送,setTimeout()以便将其值绑定到函数参数。否则,访问数组值的尝试将越界,您将获得undefined.

回答by Lucio Paiva

Delaying multiple page fetches with async/await

延迟多个页面获取 async/await

I am a big fan of the async library and I've used for a long time. However, now there's async/await. Your code becomes easier to read. For instance, this would be your main function:

我是异步库的忠实粉丝,我已经使用了很长时间。不过,现在有了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
}

Much better, isn't it? Before I get into the details of how fetchPage()and fetchUrls()work, let's first answer your question of how to wait before fetching the next page. The sleep function is pretty straightforward:

好多了,不是吗?在我详细介绍如何fetchPage()fetchUrls()工作之前,让我们首先回答您的问题,即在获取下一页之前如何等待。sleep 函数非常简单:

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

You can get a full explanation of how it works in my other answer here.

你可以得到它是如何在我的其他作品的回答充分解释在这里

Ok, back to the other functions. The requestlibrary has a promise-enabled version of it that you can use with async/await. Let's check how's fetchPage()implemented:

好的,回到其他功能。该request库有一个启用了 promise 的版本,您可以将其与async/await. 让我们检查一下是如何fetchPage()实现的:

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

Since requestis returning a promise, we can awaiton it. I also took the chance to use the transformproperty which allows us to tranform the response body before resolving the promise. I'm passing it through Cheerio, just like you did in your code.

既然request是返回一个承诺,我们就可以await了。我还借此机会使用了transform允许我们在解决承诺之前转换响应主体的属性。我正在通过 Cheerio 传递它,就像您在代码中所做的那样。

Finally, fetchUrls()can just call fetchPage()and process it to fetch your array of URLs before resolving its promise. Here's the full code:

最后,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();

To use requestwith promises, install it like this:

request与 Promise 一起使用,请像这样安装它:

npm install request
npm install request-promise-native

And then require("request-promise-native")in your code, like in the example above.

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

回答by Rodrigo Medeiros

Another alternative would be to use async.eachSeries. For example:

另一种选择是使用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();
});

回答by apsillers

Since you're already using async, async.wilstwould do nicely as a replacement for for.

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

whilstis an asynchronous while-like function. Each iteration is only run after the previous iteration has called its completion callback. In this case, we can simply postpone execution of the completion callback by 10 seconds with setTimeout.

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

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