我有一種情況,我必須從多個 api 中獲取不同次數的資料,以便將所有資料轉換為嵌套格式。基本上,我必須從一個 api 中獲取可用組的串列,然后分別為每個組獲取資料,并對任何子組重復此操作。我想出的解決方案使用遞回來獲取資料并將嵌套屬性從第一個請求添加到原始陣列:
fetch("/mygroups")
.then((res) => res.json())
.then((data) => {
return data.map((group) => ({ value: group.id, label: group.name, children: [] }));
})
.then((groups) => {
groups.forEach((group) => {
fetchGroupData(group);
});
return groups;
})
.then((groups) => {
// I need this groups variable to be the final groups variable with all it's
// nested children
functionToCallAfterAllRequestsAreMade(groups);
});
async function fetchGroupData(group) {
const res = await fetch(`/groups/${group.value}`);
const data = await res.json();
// A group can have locations and/or more groups as its children.
// if it has groups, it will call fetchGroupData for those
// child groups
const locations = data.locations.map((location) => ({
value: location.id,
label: location.location_name,
}));
const groups = data.groups.map((group) => ({
value: group.id,
label: group.name,
children: [],
}));
group.children = [...groups, ...locations];
if (groups.length > 0) {
group.children.forEach((child) => {
if (child.hasOwnProperty("children")) {
fetchGroupData(child);
}
});
}
}
問題是這段代碼似乎為初始組陣列呼叫 fetchGroupData 并添加他們的孩子,但隨后只回傳一層深度,而無需等待下一層呼叫繼續進行。fetchGroupData 函式不斷被呼叫正確的次數,但由于某種原因,我只能從第一輪呼叫中訪問陣列。我怎樣才能等到所有的電話都完成?我試過在任何地方添加一堆等待,甚至在沒有運氣的情況下為 forEach 使用 promise.all。此外,如果有一種完全不同的方式來解決這個格式化問題會更容易,那也將不勝感激。謝謝。
uj5u.com熱心網友回復:
您當前代碼的問題在于您有多個呼叫fetchGroupData(),您既沒有鏈接到現有的 promise 也沒有呼叫awaiting。因此,它們最終成為獨立的承諾鏈,與.then()您要檢查最終資料的位置完全沒有任何聯系。
因此,要修復它,您必須將所有新 Promise 鏈接到現有的 Promise 鏈中,以便它們都連接在一起。有多種方法可以做到。由于您似乎在傳遞每個人都在使用的一種通用資料結構groups,因此我選擇了await在所有其他異步呼叫上使用的方法,以強制對所有內容進行排序。可能可以并行化兩個回圈并使用Promise.all(),但是您groups在每個子呼叫中修改單個資料結構的方式意味著您無法保留任何順序,因此不并行運行似乎更安全。
請注意,我還擺脫了.forEach()回圈,因為它們不具有承諾感知能力,并且我將 更改為具有for承諾感知能力的常規回圈,并將使用await.
fetch("/mygroups")
.then((res) => res.json())
.then(async (data) => {
const groups = data.map((group) => ({ value: group.id, label: group.name, children: [] }));
for (let group of groups) {
await fetchGroupData(group);
}
return groups;
})
.then((groups) => {
// I need this groups variable to be the final groups variable with all it's
// nested children
functionToCallAfterAllRequestsAreMade(groups);
});
async function fetchGroupData(group) {
const res = await fetch(`/groups/${group.value}`);
const data = await res.json();
// A group can have locations and/or more groups as its children.
// if it has groups, it will call fetchGroupData for those
// child groups
const locations = data.locations.map((location) => ({
value: location.id,
label: location.location_name,
}));
const groups = data.groups.map((group) => ({
value: group.id,
label: group.name,
children: [],
}));
group.children = [...groups, ...locations];
if (groups.length > 0) {
for (let child of group.children) {
if (child.hasOwnProperty("children")) {
await fetchGroupData(child);
}
}
}
}
變化:
- 合并來自兩個
.then()處理程式的代碼,因為其中一個包含純同步代碼。創建一個.then()處理程式,async以便我們可以await在其中使用,這意味著它將回傳一個承諾,該承諾將通過從.then()處理程式回傳的方式掛接到此承諾鏈中。 - 更改
groups.forEach(...)為,for (let group of groups) { ... }以便我們可以使用await和暫停回圈。 - 更改
fetchGroupData(group);為await fetchGroupData(group); - 在
fetchGroupData()變化group.children.forEach((child) => { ...})到for (let child of group.children) { ... });再這樣我們就可以使用await暫停回圈。 - 在
fetchGroupData()更改fetchGroupData(group);為await fetchGroupData(group);
這確保fetchGroupData()回傳的承諾在所有遞回呼叫都解決之前不會解決。并且,它確保呼叫它的人也在觀察它回傳的承諾,并且在該承諾解決之前不會前進。這將所有內容連接到一個巨大的承諾鏈(和await鏈)中,以便正確排序,并且您可以functionToCallAfterAllRequestsAreMade(groups);在一切完成后呼叫。
uj5u.com熱心網友回復:
我建議將您的功能分解為單獨且可管理的部分-
function getJson(url) {
return fetch(url).then(r => r.json())
}
我們寫fetchGroups(復數)來獲取所有組 -
async function fetchGroups(url) {
const groups = await getJson(url)
return Promise.all(groups.map(fetchGroup))
}
其中fetchGroup(單數)獲取單個組 -
async function fetchGroup({ id, name, locations = [], groups = [] }) {
return {
value: id,
label: name,
children: [
...locations.map(l => ({ value: l.id, label: l.location.name })),
...await fetchGroups(`/groups/${id}`)
]
}
}
這種函式排列稱為相互遞回,非常適合創建、遍歷和操作遞回結構,例如問題中的樹。
現在我們只需呼叫fetchGroups您的初始路徑 -
fetchGroups("/mygroups")
.then(console.log, console.error)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/398425.html
標籤:javascript 异步 递归 异步等待 承诺
上一篇:如何在python多處理map_async函式中共享字典?
下一篇:2021年度微信公眾號文集
