Node中并行/异步的多个分页GET API调用
Posted
技术标签:
【中文标题】Node中并行/异步的多个分页GET API调用【英文标题】:Multiple paginated GET API calls in parallel/async in Node 【发布时间】:2019-03-21 09:30:17 【问题描述】:我正在调用 bitbucket API 以获取存储库中的所有文件。我已经达到了可以获取存储库中所有文件夹的列表的地步,并对存储库中的所有根文件夹进行第一次 API 调用,并获取所有文件夹的前 1000 个文件的列表。
但问题是 bitbucket api 一次只能给我每个文件夹 1000 个文件。
我需要附加一个查询参数 &start =nextPageStart 并再次调用,直到它为空并且每个 API 的 isLastPage 为真。我怎样才能用下面的代码实现呢??
我从第一次调用 api 得到 nextPageStart。请参阅下面的 API 响应。
下面是我目前的代码。
感谢任何帮助或指导。
来自每个文件夹调用的单个 API 的响应。
"values": [
"/src/js/abc.js",
"/src/js/efg.js",
"/src/js/ffg.js",
...
],
"size": 1000,
"isLastPage": false,
"start": 0,
"limit": 1000,
"nextPageStart": 1000
我进行异步调用以获取文件列表的函数
export function getFilesList()
const foldersURL: any[] = [];
getFoldersFromRepo().then((response) =>
const values = response.values;
values.forEach((value: any) =>
//creating API URL for each folder in the repo
const URL = 'https://bitbucket.abc.com/stash/rest/api/latest/projects/'
+ value.project.key + '/repos/' + value.slug + '/files?limit=1000';
foldersURL.push(URL);
);
return foldersURL;
).then((res) =>
// console.log('Calling all the URLS in parallel');
async.map(res, (link, callback) =>
const options =
url: link,
auth:
password: 'password',
username: 'username',
,
;
request(options, (error, response, body) =>
// TODO: How do I make the get call again so that i can paginate and append the response to the body till the last page.
callback(error, body);
);
, (err, results) =>
console.log('In err, results function');
if (err)
return console.log(err);
//Consolidated results after all API calls.
console.log('results', results);
);
)
.catch((error) => error);
【问题讨论】:
下一页的网址在哪里?或者说isLastPage = false
时会怎样形成。
如果 isLastPage 为 false,我需要将查询参数附加到 URL &start =nextPageStart
【参考方案1】:
我能够通过创建带有回调的函数来使其正常工作。
export function getFilesList()
const foldersURL: any[] = [];
getFoldersFromRepo().then((response) =>
const values = response.values;
values.forEach((value: any) =>
//creating API URL for each folder in the repo
const URL = 'https://bitbucket.abc.com/stash/rest/api/latest/projects/'
+ value.project.key + '/repos/' + value.slug + '/files?limit=1000';
foldersURL.push(URL);
);
return foldersURL;
).then((res) =>
// console.log('Calling all the URLS in parallel');
async.map(res, (link, callback) =>
const options =
url: link,
auth:
password: 'password',
username: 'username',
,
;
const myarray = [];
// This function will consolidate response till the last Page per API.
consolidatePaginatedResponse(options, link, myarray, callback);
, (err, results) =>
console.log('In err, results function');
if (err)
return console.log(err);
//Consolidated results after all API calls.
console.log('results', results);
);
)
.catch((error) => error);
function consolidatePaginatedResponse(options, link, myarray, callback)
request(options, (error, response, body) =>
const content = JSON.parse(body);
content.link = options.url;
myarray.push(content);
if (content.isLastPage === false)
options.url = link + '&start=' + content.nextPageStart;
consolidatePaginatedResponse(options, link, myarray, callback);
else
// Final response after consolidation per API
callback(error, JSON.stringify(myarray));
);
【讨论】:
【参考方案2】:我认为最好的方法是将它包装在一个老式的 for 循环中(forEach 不适用于异步,因为它是同步的,它会导致所有请求同时产生)。
我的理解是,您在获取values
数组的地方执行某种启动查询,然后您应该在页面之间进行迭代。这里有一些代码,我没有完全掌握 API,所以我将给出一个简化(并且希望可读)的答案,你应该能够适应它:
export async function getFilesList()
logger.info(`Fetching all the available values ...`);
await getFoldersFromRepo().then( async values =>
logger.info("... Folders values fetched.");
for (let i = 0; ; i++ )
logger.info( `Working on page $i`);
try
// if you are using TypeScript, the result is not the promise but the succeeded value already
const pageResult: PageResult = await yourPagePromise(i);
if (pageResult.isLastPage)
break;
catch(err)
console.err(`Error on page $i`, err);
break;
logger.info("Done.");
);
logger.info(`All finished!`);
背后的逻辑是首先getFoldersFromRepo()
返回一个返回值的promise,然后我通过yourPagePromise
函数(返回一个promise)依次迭代所有可用页面。 async/await 结构允许编写更具可读性的代码,而不是使用 then() 的瀑布。
我不确定它是否尊重您的 API 规范,但它是您可以用作基础的逻辑! ^^
【讨论】:
谢谢,我也会尝试这种方式。我终于想出了一个分页的方法。我会试试你的方法,看看哪个表现更好。 我使用了一些糖语法来避免嵌套,只需注意映射承诺或在没有await
的情况下简单地循环它们,因为您将同时生成所有它们(在这里根据经验说话,我用 2000 个请求轰炸了我的服务并杀死了它!:P)以上是关于Node中并行/异步的多个分页GET API调用的主要内容,如果未能解决你的问题,请参考以下文章
当您不知道页数时,如何使用 Node.js 在 while 循环中向 API 发出多个分页 GET 请求?
如何使用 asyncio 和 aiohttp 异步通过 api 响应进行分页