提问人:theimpatientcoder 提问时间:10/10/2023 最后编辑:theimpatientcoder 更新时间:10/19/2023 访问量:286
使用 Promise 通过 axios 获取分页响应
Using Promise to get paginated response using axios
问:
我正在尝试弄清楚如何使用和/从具有一些分页逻辑返回数据的 rest API 中获取数据。Promise
async
await
具体来说,我正在尝试使用 MS Graph API 获取 Azure 用户数据。由于 Azure 不会在单个 API 中发送与用户相关的所有数据,因此我们必须进行多次调用才能为单个 Azure 用户生成数据。
为了深入了解我的代码/逻辑,以下是我如何使用分页逻辑获取用户的基本数据:
let nextPageLink: string| null = null;
do {
let pagedResult: AggregationPageResult = await this.fetchNextPage(nextPageLink);
let usersMap: Map<string, any>[] = pagedResult.data!;
nextPageLink = pagedResult.nextPageLink!;
this.sendPageResult(usersMap, res);
} while(nextPageLink)
这里的分页逻辑很简单,不会打扰我。假设对用户图形 API 的第一次调用返回 100 个用户。现在,对于这 100 个用户中的每一个,我必须单独调用以获取他们的组成员身份数据、目录角色数据、上次登录活动和其他“N”个详细信息。
以下是我用于获取当前页面中每个用户的组成员身份详细信息的代码:
for(let user of usersToSearch) {
let groupMembershipAPI = "https://graph.microsoft.com/v1.0/users/" + user + "/memberOf/microsoft.graph.group";
let httpConfig: HttpRequestConfig = {
baseURL: groupMembershipAPI,
method: 'GET',
queryFilter: filters
}
executions.push(httpClient.executeRequest(httpConfig, true));
}
let executionResults: HttpResponse [] = await Promise.all(executions);
现在,此代码工作正常,并获取所有用户的组成员身份详细信息,如果成员身份较少,或者换句话说,成员身份数据适合默认页面大小或任何页面大小。如果数据更多,如果任何特定用户有 1000 个组成员身份,而默认页面大小仅为 100,该怎么办?在这种情况下,我当前的逻辑只会带来 100 个成员资格,因为我没有在下一页执行它
对于我推送的所有请求,我还想注意 API 是否发送了有效的 nextPageLink,以便代码再次获取该用户的成员资格数据,直到 nextPageLink 为空。executions
现在,这只是组成员身份的一个方案,请考虑如果我想引入所有其他数据(如目录角色、PIM 数据、上次登录活动等)会发生什么情况!
用于引入用户页面的原始分页逻辑也可以在这里使用,但我正在寻找一种解决方案,我可以递归或动态地添加 Promise 链,或者是否有任何其他机制可以帮助我以更好的方式解决它?我还想确保性能和资源利用率
编辑1:我打算在获取用户属性(例如其组成员身份/角色等)时使用不同的分页方法,这是因为:
- 我想按顺序同步处理用户页面
- 相反,在获取这些属性时,它们可以并行运行。
- 这意味着,我想实现的是,获取用户组成员资格属性可以与获取用户角色并行运行......
- 我不想等待开始角色提取,因为用户组提取正在进行中。两者都可以并行运行
- 如果我使用原始方法,那么这将是顺序的。
伪代码来解释我的以上几点:
async public fetchNextPage(nextPageLink: string): AggregationPageResult {
let propertyFetchResults = []
1. await fetch the next user page
2. for every extra property to be fetched with pagination
2.a propertyFetchResults.push (...result)
3. await Promise.all(propertyFetchResults)
4. merge the above property results with users of current page
}
答:
- 首先,您需要了解是否真的需要使用 do/while 获取所有页面?在大多数情况下,只有当用户滚动到底部或单击下一页时,您才需要获取下一页。
此外,请确保在加载用户页面时需要获取所有其他数据。如果具有用户列表和用户详细信息屏幕,则可能不需要列表屏幕上的所有数据,并且只能在打开用户详细信息时获取其他数据。
- 如果可能,首先聚合 ID(例如,获取页面上所有用户的所有组 ID),将它们置于“设置”以删除重复项,然后提取。使用接受数组而不是一个 id 的 API,例如 getGroups(ids) 而不是多个 getGroup(id):
const fetchUsers = async (page = 1) => {
const users = await fetch(...)
const promises = []
// fetch groups
const groups = []
const groupIds = Array.from(new Set(users.map(x => x.groupId)))
while (groupIds.length) {
const idsToFetch = groupIds.splice(0, 100) // Splice is used here if 100 is a limit of ids for this API
promises.push(fetchGroups(idsToFetch).then(x => groups.push(...x)))
}
// fetch roles
const roles = []
const roleIds = Array.from(new Set(users.map(x => x.roleId)))
while (roleIds.length) {
const idsToFetch = roleIds.splice(0, 100) // Splice is used here if 100 is a limit of ids for this API. Makes sense only if there are more than 100 roles.
promises.push(fetchRoles(idsToFetch).then(x => roles.push(...x)))
}
...
await Promise.all(promises)
return {
users,
groups,
roles,
}
}
如果带有数组 ids 参数的 API 不可用,请使用分页方法加载所有数据。do/while
请记住错误处理。如果一个请求失败怎么办?您想使用 AbortController 取消所有其他请求,还是重试几次失败的请求,或者忽略等?
请记住缓存 - 缓存中可能已经有一些实体,如果它们足够新鲜,您可以跳过再次提取它们。
不要在没有充分理由的情况下将实体(非规范化)合并在一起。在大多数情况下,最好单独缓存数据,规范化数据,并在 id 需要时稍后访问:
const group = store.getState().entities.groups[user.groupId]
它将保持状态一致,每个实体只有一个实例。当它在下次获取后在缓存中更新时,它将在整个应用程序 UI 中更新,而不仅仅是一个特定的屏幕。
但是,如果您认为规范化现在太多了,而且可能是,请随意跳过它,在获取时合并数据。
- Axios 不是必须拥有的库,大多数开发人员甚至无法解释他们为什么使用它。
评论
data
nextPageLink