【问题标题】:Node js how to run axios.get every 2 seconds?Node js如何每2秒运行一次axios.get?
【发布时间】:2019-07-03 19:44:19
【问题描述】:

我是 node js 的新手,这就是我想要做的事情:我正在通过 map 函数(大约 3000 个链接)循环一个包含我们网站链接的 json 文件,在循环内我是为每个链接做一个 axios get 并获取响应状态码(将来会做其他事情)。但我只想每 2 秒或 5 秒运行一次 axios,否则我会压倒网络服务器。我正在尝试输入异步等待,但它仍然太快并且服务器正在受到打击(我在技术上 DDos-ing 我自己的网站)。我在 axios 周围放置了一个 SetTimeout ,但这似乎不起作用,因为在控制台中结果打印速度太快了。 所以问题是,如何让每个 axios.get 请求在 map 循环中运行前每 2 秒等待一次?

var axios = require('axios');
const fs = require('fs');
var statusCheck = 0;

var main = [];
let rawdata = fs.readFileSync('C:/Users/jay/Documents/crawl/filtered2.json');
let jsonParsed = JSON.parse(rawdata);

jsonParsed.map(async(line) => {

    var encodeLink = encodeURI(line.link);
    const response = await axios.get(encodeLink).catch((err) => {
        var Status_ErrorsCatchaxios = {
            "status Code": err.response.status ? err.response.status : "No status code available",
            "Page title:": $('title').text() ? $('title').text() : 'No title avaialble',
            "Original Link": encodeLink ? encodeLink : "No Original Link Available",
            "errorCode": err
        }
        main.push(Status_ErrorsCatchaxios)
    })
    try {
        console.log(response.status)

        statusCheck = statusCheck + 1;
        console.log("Link: ", statusCheck)
    } catch (error) {
        console.log(error)
    }
})

【问题讨论】:

    标签: javascript node.js asynchronous axios


    【解决方案1】:

    [].map 函数不会等待您的项目解决,因此您的代码当前正在并行调度所有请求(如您所说,大约 3000 个)。

    您可以改用for...of 来一次只运行一个请求。例如:

    async function makeRequests (lines) {
      for (const line of lines) {
        const encodedLink = encodeURI(line.link)
        const response = await axios.get(encodedLink)
        // ...your response handling code here...
      }
    }
    
    makeRequests(jsonParsed)
    

    如果您想在每个请求之间等待 2s,您可以在 for...of 循环中添加这行代码:

    await new Promise(resolve => setTimeout(resolve, 2000))
    

    更好的解决方案

    上述解决方案有效,但我假设您的网络服务器一次可能会处理多个请求,因此理想的情况可能是将您的代码限制为最多发出 N 个请求在给定时间并行。这样,您不会淹没您的服务器,但您能够比一次只执行一个请求更快地获得结果。

    bluebird NPM 模块允许您使用他们的Promise.map 函数来做到这一点。

    此函数接收您的项目列表作为第一个参数,一个执行某些操作并为每个项目返回一个承诺的函数作为第二个参数,以及一个带有 concurrency 键的对象,描述您希望允许多少项目作为第三个参数并行处理。

    它的工作原理如下:

    const bluebird = require('bluebird')
    
    async function makeRequests (lines) {
      await bluebird.map(
        lines,
        async (line) => {
          const encodedLink = encodeURI(line.link)
          const response = await axios.get(encodedLink)
          // ...your response handling code here...
        },
        { concurrency: 3 }
      )
    }
    
    makeRequests(jsonParsed)
    

    【讨论】:

    • 哇,这似乎可行,但代码似乎卡在第 301 个链接附近?它并不总是 301 有时 180 有时 402。知道为什么吗?
    • @jsPlayer 我能看到它在结束前停止的唯一原因是如果其中一个请求失败,或者您的部分代码在处理特定请求的响应时抛出错误(比如尝试读取null 的一些属性)。尝试添加 try-catch 语句来处理错误。
    【解决方案2】:

    放弃map,替换为for ... of,等待需要2秒才能解决的承诺,将所有内容包装在异步IIFE中以使等待合法。

    // dummy data
    const fakeJson = new Array(5).fill();
    const fakeRequest = () => console.log(`request at ${new Date().toUTCString()}`);
    
    // iteration with 2s in between
    (async () => {
      for (let line of fakeJson) {
        await new Promise(r => setTimeout(r, 2000));
        fakeRequest();
      }
    })()

    你也可以更经典地使用setInterval,但HTTP请求是异步的,所以不妨从一个能很好地处理异步和循环的结构开始。

    【讨论】:

      【解决方案3】:

      因为.map.forEach.reduce 等不会等待Promise 解决,所以您会同时进行所有操作。使用 Simple For 循环,它会等待每个 promise 解决或拒绝。

      for(let i=0;i<jsonParsed.length;i++) {
          var encodeLink = encodeURI(line.link);
          const response = await axios.get(encodeLink).catch(...)
          try {
              ....
          } catch (error) {
              ...
          }
      })
      

      为什么它不起作用? 如果我们模仿forEach 循环,它会是这样的,

      function forEach(arr, cb){
          for(let i=0;i<arr.length;i++){
              cb(arr[i], i, cb);
          }
      }
      

      所以你看到它不是await cb

      【讨论】:

        【解决方案4】:

        超时不会在循环中起作用的原因是因为它会在timeout 延迟之后立即触发所有请求/函数。

        这个想法是在每次迭代中放置延迟,并且只有在延迟之后才开始下一次迭代。

        您可以运行一个自调用函数,该函数在延迟后调用自身。所以要每 2 秒运行一次函数,你可以试试这个:

        let jsonParsed = JSON.parse(rawdata);
        let len = jsonParsed.length;
        
        (function requestLoop (i) {          
            setTimeout(function () {   
                let line = jsonParsed[len-i]
                var encodeLink = encodeURI(line.link);
                const response = await axios.get(encodeLink).catch((err) => {
                    var Status_ErrorsCatchaxios = {
                        "status Code": err.response.status ? err.response.status : "No status code available",
                        "Page title:": $('title').text() ? $('title').text() : 'No title avaialble',
                        "Original Link": encodeLink ? encodeLink : "No Original Link Available",
                        "errorCode": err
                    }
                    main.push(Status_ErrorsCatchaxios)
                })
                try {
                    console.log(response.status)
        
                    statusCheck = statusCheck + 1;
                    console.log("Link: ", statusCheck)
                } catch (error) {
                    console.log(error)
                }                        
                let jsonParsed = JSON.parse(rawdata);
        
                if (--i) requestLoop(i);     
           }, 2000)
        })(len);
        

        【讨论】:

          【解决方案5】:

          你可以使用

          var i=0;
          jsonParsed.map(async(line) => {
          i++
          setTimeout(async() => {
          
          },i*2000)
          }
          

          【讨论】:

            【解决方案6】:

            您可以使用 setTimeout 函数每 2 秒运行一次代码!

            setTimeout(async() => {
             // await postRequest()
            },2000)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-01-09
              • 1970-01-01
              • 2021-06-17
              • 2018-10-26
              • 2013-02-04
              • 2023-01-22
              • 2018-03-06
              • 2021-05-14
              相关资源
              最近更新 更多