javascript - 如何使用 Node JS 执行多个 API 调用
问题描述
我正在围绕 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 链接的承诺。当它少于 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 个并不断出错。
很抱歉漫无边际,但任何想法或方向都是有帮助的。
解决方案
BluebirdsPromise.map
包含并发限制。
const Promise = require('bluebird')
const getAllData = async (URLs) => {
return Promise.map(URLs, fetchData, 20)
}
推荐阅读
- python - How do I create a command using discord.py?
- python - ModuleNotFoundError:没有名为“pegasus”的模块
- java - 不兼容的类型:列表
> 无法转换为列表 - >
- ios - How to get/post/update/delete data from a mongoDB database with an iOS app?
- python - Value not right while doing cumprod() to get cumulative returns in pandas
- dart - 空安全飞镖中的可选参数是否可以自动为空?如果不是,是否有一种简单的方法可以使我的代码为空安全?
- python - CondaHTTPError: HTTP 000 CONNECTION FAILED for url
- xml - 获取 XML 节点的属性值时出错
- c++ - How to solve Undefined symbols for architecture x86_64 error when using C++ API `llvm::InitializeAllAsmParsers()` in LLVM?
- python - How to fill 2d numpy array with some 2d numpy array