[英]Async/Await for recursive API Calls
我正在嘗試從分頁端點收集所有 id。 我想等待所有調用完成,但它似乎只完成第一個調用並將數據和 promise 返回到下一個調用。
我怎樣才能等待所有的電話返回。
const getSelectAllIds = async (url) => {
let response = await axios.post(url);
if (response.status === 200 && response.data.next != null) {
console.log(`calling: ${response.config.url}`);
return [
...response.data.results,
await getSelectAllIds(response.data.next)
];
}
};
const someFunction = async (url, data) => {
const selectedIds = await getSelectAllIds(url);
return selectedIds;
};
所有async
函數總是返回 promise。 因此getSelectAllIds()
和someFunction()
都將始終返回 promise。 該調用者必須使用.then()
或await
從 promise 獲取值。 您不能讓它們同步返回異步檢索的值。 Javascript 不能那樣工作。
這就是 nodejs 中異步編碼的工作方式。 您永遠無法將異步值轉換為同步值。 您必須使用異步機制來使用異步值。 對於 Promise,這意味着調用者必須使用.then()
的await
來獲取值。
getSelectAllIds(someUrl).then(allIDs => {
console.log(allIDs);
}).catch(err => {
console.log(err);
});
請注意,如果response.status
不是 200,則不清楚您希望代碼做什么。而且,看起來您沒有從最后一頁收集數據,因為如果有,您不會將數據添加到數組中沒有data.next
。下一個。
我想你想要這個:
const getSelectAllIds = async (url) => {
const response = await axios.post(url);
if(!response.ok) throw new Error('HTTP error'); // lets not ignore these.
return [
...response.data.results,
...(response.data.next ? await getSelectAllIds(response.data.next) : [])
];
}
異步生成器
我將建議一種不同的方法,使用異步生成器。 我希望這種方法的好處是不言而喻的——
async function post (url)
{ const r = await axios.post(url)
if (r.status == 200)
return r.data // <- resolve data
else
throw r // <- reject
}
async function* getSelectAllIds (url)
{ if (url == null) return
let { config, results, next } = await post(url) // <- get
console.log(`calling: ${config.url}`) // <- debug output
yield *results // <- yield each
for await (const v of getSelectAllIds(next)) // <- recur
yield v
}
async function someFunction (url)
{ const r = [] // <- empty result
for await (const v of getSelectAllIds(url)) // <- simple iteration
r.push(v) // <- collect results
return r // <- return
}
// run and catch errors
someFunction("/my/url").then(console.log, console.error)
演示
我想以一種您可以在自己的瀏覽器中驗證結果的方式來演示這一點。 為此,我們將制作一個假DB
-
const DB =
{ "/1":
{ config: { url: "/1" }
, results: [ "foo", "bar" ]
, next: "/2"
}
, "/2":
{ config: { url: "/2" }
, results: [ "cat", "dog" ]
, next: "/3"
}
, "/3":
{ config: { url: "/3" }
, results: [ "pea", "yam" ]
, next: null
}
}
接下來我們需要制作FAKE
,它是axios.post
的替代品 -
async function FAKE (url) // <- fake axios.post
{ await sleep (1000)
if (url in DB)
return { status: 200, data: DB[url] } // <- found resource
else
return { status: 404, data: null } // <- notfound resource
}
這取決於一點sleep
function,模擬延遲 -
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
要運行演示,我們只需將axios.post
替換為我們的FAKE
-
async function post (url)
{ const r = await FAKE(url) // <- fake axios.post
if (r.status == 200)
return r.data
else
throw r
}
someFunction("/1").then(console.log, console.error)
展開下面的代碼段以驗證瀏覽器中的結果 -
const sleep = ms => // <- sleep for demo new Promise(r => setTimeout(r, ms)) async function FAKE (url) { await sleep (1000) // <- simulated delay if (url in DB) return { status: 200, data: DB[url] } else return { status: 404, data: null } } async function post (url) { const r = await FAKE(url) // <- fake axios.post if (r.status == 200) return r.data else throw r } async function* getSelectAllIds (url) { if (url == null) return let { config, results, next } = await post(url) console.log(`calling: ${config.url}`) yield *results for await (const v of getSelectAllIds(next)) yield v } async function someFunction (url) { const r = [] for await (const v of getSelectAllIds(url)) r.push(v) return r } const DB = {"/1":{config:{url:"/1"},results:[ "foo","bar" ],next:"/2"},"/2":{config:{url:"/2"},results:[ "cat","dog" ],next:"/3"},"/3":{config:{url:"/3"},results:[ "pea","yam" ],next:null}} someFunction("/1").then(console.log, console.error)
calling: /1
calling: /2
calling: /3
=> [ "foo", "bar", "cat", "dog", "pea", "yam" ]
沒有發電機
如果您選擇不使用異步生成器,您可以編寫一個簡單的異步 function。 這種方法的一個優點是它完全跳過了對someFunction
的需求,但是它有一個明顯的缺點,即它會在您開始使用它們之前等待所有結果 -
async function getSelectAllIds (url)
{ if (url == null) return [] // <-
let { config, results, next } = await post(url)
console.log(`calling: ${config.url}`)
return [ ...results, ...await getSelectAllIds(next) ] // <-
}
getSelectAllIds("/1").then(console.log, console.error)
calling: /1
calling: /2
calling: /3
=> [ "foo", "bar", "cat", "dog", "pea", "yam" ]
將此與使用異步生成器的結果進行比較,我們可以在結果異步到達時使用它們 -
async function someFunction (url)
{ for await (const v of getSelectAllIds(url)) // <- async generator
console.log(v)
return "done"
}
someFunction("/1").then(console.log, console.error)
calling: /1
foo
bar
calling: /2
cat
dog
calling: /3
pea
yam
done
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.