将对异步 JavaScript 函数返回的 Promise 的 .then() 调用推迟到函数内运行多个 await 之后

Postponing .then() calls on a Promise returned from an async JavaScript function until after multiple awaits have run inside the function

提问人:XDR 提问时间:5/26/2023 最后编辑:XDR 更新时间:5/26/2023 访问量:49

问:

我正在使用异步 JavaScript http 客户端从初始 URI 和后续页面 URI 下载和处理一些分页的 JSON 数据。

如果有更多页面,则每个 JSON 页面都包含下一页的 URI。

有时,我需要处理初始 URI 及其后续页面 URI 的所有页面,然后再进入下一个全新的初始 URI。

我更喜欢使用对异步 http 客户端的调用来处理初始 URI 和所有后续页面 URI,所有这些都在对我自己的函数(命名)的单个调用中,仅在第一个初始 URI 及其所有页面被下载和处理后调用返回的 FROM 来处理第二个初始 URI, 而不必将回调传递给它,我必须在 的末尾手动调用。awaitasyncf().then()Promisef()f()f()

例如,是否可以修改和/或在下面使用它的代码,以便仅在处理完所有页面后进行处理,而无需诉诸 ?f()'uri2''uri1'g()

async function f(initialPageUri) {
  let nextPageUri = initialPageUri
  while (nextPageUri) {
    const o = await getObjectFromJsonFromUri(nextPageUri)
    // do stuff with o
    nextPageUri = o.nextPageUri
  }
}

f('uri1').then(() => f('uri2'))

async function g(initialPageUri, then) {
  let nextPageUri = initialPageUri
  while (nextPageUri) {
    const o = await getObjectFromJsonFromUri(nextPageUri)
    // do stuff with o
    nextPageUri = o.nextPageUri
  }
  then()
}

g('uri1', () => g('uri2', () => {}))

从我的真实代码简化日志记录:按请求顺序:<url letter> <page number or _ if no more pages>

A 1
A _
B 1
C 1
D 1
E 1
F 1
E _
D _
B 2
C _
F 2
B 3
F _
B _
javascript async-await ecmascript-2017

评论

0赞 Wiktor Zychla 5/26/2023
这看起来已经不错了。在循环终止之前,with inside 不会解析该函数。不知道这里有什么问题。whileawait
1赞 Zac Anger 5/26/2023
循环怎么样?将等传递给并具有高于该级别的循环,而 (a for...的,可能)会让你完全删除。['uri1', 'uri2']fg
0赞 trincot 5/26/2023
问题是什么?您是否声称在所有处理都已完成之前就已执行?如果不是,问题是什么?f('uri1').then(() => f('uri2'))f('uri2')f('uri1')
0赞 XDR 5/26/2023
f('url2')确实在所有完成之前执行。f('url1')
0赞 XDR 5/26/2023
@ZacAnger 我宁愿不要将自己限制在 URI 列表作为参数,因为在为第一个 URI 处理完所有页面后,我可能想做一些事情,而不是使用不同的 URI 调用(例如,.f()f()f('uri1').then(() => bar()))

答:

1赞 RAllen 5/26/2023 #1

您的第一个解决方案应按原样工作。

默认情况下,任何函数都返回 Promise。您可以像在第一个代码块中使用 .async.thenasyncf

此外,您可以对函数执行操作,等待解析后再继续。这正是您在调用 时所做的。awaitasyncPromisegetObjectFromJsonFromUri

快速演示:

function delay(msec) {
  return new Promise((resolve) => {
    setTimeout(resolve, msec);
  });
}

let pages = 0;

async function getObjectFromJsonFromUri(uri) {
  console.log('requesting ', uri);
  await delay(1000);
  return {
    nextPageUri: pages-- > 0 ? `page${pages}` : null,
  };
}

async function f(initialPageUri) {
  pages = 2;  // simulate that each uri has 2 pages
  let nextPageUri = initialPageUri
  while (nextPageUri) {
    const output = await getObjectFromJsonFromUri(nextPageUri);
    // do stuff with output
    nextPageUri = output.nextPageUri
  }
}

f('uri1').then(() => f('uri2'));

评论

0赞 XDR 5/26/2023
f('url2')在所有完成之前执行。它似乎在第一个 s 之后开始运行,但可能在随后的 s 之前开始运行。awaitsf('url1')awaitawait
0赞 RAllen 5/26/2023
@XDR我添加了一个演示代码来展示它是如何工作的。检查是否有帮助,如果您仍有问题,请告诉我。
0赞 Wiktor Zychla 5/26/2023
@XDR:如果没有可重复的例子,很难活下去。 不应该之前执行,假设你的这个特定结构代码。f('url2')
1赞 XDR 5/26/2023
@RAllen我一直在简化我的代码,去掉功能和组织部分,以更接近本质,而没有其他填充方式。在将所有回调参数替换为包装函数以仅调用基础函数后,我现在获得了正确的请求顺序。我需要将这方面的进一步工作推迟 8 个小时,但我想我应该能够弄清楚为什么包装器函数会导致问题。我稍后会回复解决方案,或者如果我无法找到解决方案,我会提出问题。感谢您的帮助!awaitthen()
1赞 XDR 5/26/2023
@RAllen 包装函数没有从下载和处理 JSON 的 fetch 代码返回。一旦该函数返回 ,一切都正确排序。感谢您的帮助。PromisePromise