如何延迟async函数返回Promise的.then()调用至内部多await执行完成
我现在用异步JavaScript HTTP客户端从初始URI及后续分页URI下载并处理分页JSON数据,每个JSON分页如果有更多页面,会包含下一页的URI。有时候需要处理完某个初始URI的所有分页后,再处理下一个全新的初始URI。我希望在自定义async函数f()里通过await调用异步HTTP客户端处理初始URI及所有后续分页,通过在f()返回的Promise上调用.then(),确保第一个初始URI的所有分页处理完成后再处理第二个,不用像g()那样手动传递回调函数。比如能不能修改f()及其调用代码,让uri2仅在uri1的所有分页处理完成后才执行,而不用g()的手动回调机制?
示例代码:
async function f(initialPageUri) { let nextPageUri = initialPageUri while (nextPageUri) { const o = await getObjectFromJsonFromUri(nextPageUri) // 处理o的逻辑 nextPageUri = o.nextPageUri } } f('uri1').then(() => f('uri2')) async function g(initialPageUri, then) { let nextPageUri = initialPageUri while (nextPageUri) { const o = await getObjectFromJsonFromUri(nextPageUri) // 处理o的逻辑 nextPageUri = o.nextPageUri } then() } g('uri1', () => g('uri2', () => {}))
真实代码的简化请求日志(格式为<URL标识> <页码或无更多页面时为_>):
A 1 A _ B 1 C 1 D 1 E 1 F 1 E _ D _ B 2 C _ F 2 B 3 F _ B _
你写的f('uri1').then(() => f('uri2'))已经完全能实现需求——async函数本身会返回一个Promise,只有当f('uri1')里的while循环彻底执行完毕(也就是uri1的所有分页都处理完成),这个Promise才会变成resolved状态,此时.then()里的f('uri2')才会启动,完全不需要像g()那样手动传回调。
从你提供的日志来看,实际运行中出现了多个初始URI(B、C、D、E、F)第一页并行请求的情况,这说明你大概率是同时调用了多个f()实例,比如:
// 这种写法会导致并行请求 f('B') f('C') f('D') f('E') f('F')
如果要让这些初始URI按顺序逐个处理所有分页,有两种简洁的实现方式:
方式1:链式调用.then()
f('A') .then(() => f('B')) .then(() => f('C')) .then(() => f('D')) .then(() => f('E')) .then(() => f('F'))
方式2:用for...of循环(更易读维护)
如果初始URI是数组形式,比如const uris = ['A', 'B', 'C', 'D', 'E', 'F'],可以用async函数包裹循环,逐个await:
async function processAllUris(uris) { for (const uri of uris) { await f(uri) } } processAllUris(['A', 'B', 'C', 'D', 'E', 'F'])
这样就能保证每个初始URI的所有分页都处理完成后,才会开始处理下一个初始URI,日志会变成严格按顺序执行的样子:
A 1 A _ B 1 B 2 B 3 B _ C 1 C _ D 1 D _ E 1 E _ F 1 F 2 F _
内容的提问来源于stack exchange,提问作者XDR

