异步生成器
我将建议一种不同的方法,使用异步生成器。我希望这种方法的好处是不言而喻的 -
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 函数,来模拟延迟-
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)
展开下面的 sn-p 以在浏览器中验证结果 -
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" ]
没有生成器
如果您选择不使用异步生成器,您可以编写一个简单的异步函数。这种方法的一个优点是它完全不需要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