【问题标题】:How to execute multiple API calls using Node JS如何使用 Node JS 执行多个 API 调用
【发布时间】:2020-07-28 10:16:10
【问题描述】:

我正在围绕 Eve Online 的 ESI api 构建一个站点。我正在使用 NodeJS 使用 axios 进行异步获取调用。我正在尝试使用他们的 api 获取各种游戏区域的市场数据。我已经成功地获取数据并将其添加到我的数据库中,但我觉得我可以以更好更快的方式进行这些调用。他们的 api 在页面中返回结果,每页有 1000 个结果。对于某些地区,我必须请求多达 300 多页,但通常是 100 页。我一直在使用 for 循环执行此操作,然后只检查结果数组是否有数据,如果没有,我会中断 for 循环。对于某些地区,这仅需要几秒钟 10-20 页,而对于其他地区则需要几分钟 50 多页。我正在努力加快速度。现在下载所有内容并上传到我的数据库大约需要 60 分钟,而下载需要花费大量时间。

这是我当前获取数据的代码。我首先得到一个包含所有游戏区域 ID 的列表,然后使用 for 循环遍历每个区域。使用当前区域 ID,我使用它来获取该区域中所有类型的项目 ID。这是我的第一页问题出现的地方。每个区域可以有 1 件商品或 15000 件商品。所以我选择 50 作为任意数字循环直到,检查每个调用是否返回数据。然后我使用相同的区域 ID 执行相同的操作来获取所有订单。这是我的下载时间受到影响的地方。一些地区的订单很少或更多,其中一个地区有 300 多页,在我的笔记本电脑上需要 12 分钟,而在 AWS 设置上需要 4-5 分钟。

       const regionData = await axios.get(
                "https://esi.evetech.net/latest/universe/regions/?datasource=tranquility"
            );
//Eg region 10000042
            const region = regionData.data;
            console.log(region);
            for (let r = 0; r < region.length; r++) {
                let typeID = [];
                let orders = [];
        
                for (let i = 1; i < 50; i++) {
                    const types = await axios.get(
                        `https://esi.evetech.net/latest/markets/${region[r]}/types/?datasource=tranquility&page=${i}`
                    );
                    if (types.data.length) {
                        for (let x = 0; x < types.data.length; x++) {
                            typeID.push(types.data[x]);
                        }
                    } else {
                        break;
                    }
                }
    
                for (let i = 1; i < 500; i++) {
                    const results = await axios.get(
                        `https://esi.evetech.net/latest/markets/${region[r]}/orders/?datasource=tranquility&order_type=all&page=${i}`
                    );
                    if (results.data.length) {
                        for (let x = 0; x < results.data.length; x++) {
                            orders.push(results.data[x]);
                        }
                    } else {
                        break;
                    }
                }
    
    ... code to sort through data and upload to database
    
    }

我想加快这个下载过程,这样我就不必等待每个页面一次下载一个。

我正在尝试使用异步承诺一次下载多个页面,但由于每个区域的页面数量不同,我刚刚选择了一个适合所有人的最大数量,这导致承诺一次进行 300 多个调用,这只是中断并导致 500 错误。当我将页数限制为小于 100 时,它的效果非常好,并且可以在我的 for 循环所用时间的一小部分内获取该数据。我只是不知道如何实现一种方法来循环并获取所有数据,而无需尝试一次请求 300 个页面。

这是我正在测试的当前代码,它使用为类型和订单映射一组 api 链接的 Promise。当它少于 100 页时效果很好,但我开始收到 500 个服务器错误。

let urlsR = [];
    let urlsT = [];
    
    const getAllData = async (URLs) => {
        return Promise.all(URLs.map(fetchData));
        
    };
    function fetchData(URL) {
        return axios
            .get(URL)
            .then(function (response) {
                return response.data;
            })
            .catch(function (error) {
                console.log(error)
                 //return { success: false };
            });
    }
    const regionData = await axios.get(
        "https://esi.evetech.net/latest/universe/regions/?datasource=tranquility"
    );
    const region = regionData.data;
    for (let r = 0; r < region.length; r++) {
        console.log("Starting API fetch for " + region[r]);
        urlsR = [];
        urlsT = [];
        for (let i = 1; i < 17; i++) {
            urlsT.push(
                `https://esi.evetech.net/latest/markets/${region[r]}/types/?datasource=tranquility&page=${i}`
            );
        }
        for (let i = 1; i < 316; i++) {
            urlsR.push(
                `https://esi.evetech.net/latest/markets/${region[r]}/orders/?datasource=tranquility&order_type=all&page=${i}`
            );
        }
        let getTypes = await getAllData(urlsT);
        let getMarket = await getAllData(urlsR);
        let orders = [];
        
... Code that sorts through data and uploads to DB
}

我的问题是无法检查每个区域需要请求多少页面,因此我再次选择了一个上限,并且每个区域都针对 api 请求那么多页面。如果没有找到结果,api 只会返回一个空数组,这样可以避免因错误而破坏代码。我只是不想进行 100 次 api 调用,只获得 15 个页面和 85 个空数组,或者尝试一次执行 300 个并不断出错。

抱歉漫无边际,但任何想法或方向都是有帮助的。

【问题讨论】:

  • 有什么方法可以确定您可以从getMarket 变量中接收到多少页?它是否为每个页面请求存储一个数组?
  • 它会为我要求的每个页面获得一个结果,即使没有数据它只是返回一个空数组,因此它将一个空数组推送到变量。所以getMarket 无论如何都会获得 315 页。

标签: javascript node.js api es6-promise


【解决方案1】:

Bluebirds Promise.map 包含并发限制。

const Promise = require('bluebird')

const getAllData = async (URLs) => {
  return Promise.map(URLs, fetchData, 20)
}

【讨论】:

    【解决方案2】:

    这里有一个解决方案:

    
      let getMarket = [];
      let maxPageLimit = 300;
      let cycle = 50;
      for (let i = 1; i < maxPageLimit; i += cycle) {
        urlsR = [];
        for (let j = i; j < i + cycle; j++) {
          urlsR.push(
            `https://esi.evetech.net/latest/markets/${region[r]}/orders/?datasource=tranquility&order_type=all&page=${i}`
          );
        }
        getMarket = getMarket.concat(await getAllData(urlsR));
        if (len(getMarket[len(getMarket - 1)]) === 0) {
          break;
        }
      }
    

    它基本上实现了一个 50 页的循环。每 50 页后,它会检查它收到的最后一页是否为空。如果是,它会中断 fetch 循环。您可以同时设置循环量和要迭代的最大页数。

    根据您使用的循环量,这将大大减少浪费的呼叫。

    【讨论】:

    • 谢谢!这帮助我思考如何更改一些逻辑来更改某些区域的循环数。
    猜你喜欢
    • 2020-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    相关资源
    最近更新 更多