英文:
Postponing .then() calls on a Promise returned from an async JavaScript function until after multiple awaits have run inside the function
问题
I am using an asynchronous JavaScript http client to download & process some paged JSON data from an initial URI & subsequent page URIs.
Each JSON page includes the URI for the next page, if there are more pages.
Sometimes, I need to process all pages for an initial URI & its subsequent page URIs before progressing to the next completely new initial URI.
I'd prefer to use await calls to the async http client to handle the initial URI & all subsequent page URIs all within a single call to my own async function (named f()), calling .then() on the Promise returned from f() to process a second initial URI only after the first initial URI & all of its pages have been downloaded & processed, rather than having to pass a callback to f() that I must manually call at the end of f().
e.g., can f() and/or the code that uses it below be modified such that 'uri2' will only be processed after all pages of 'uri1' have been processed without resorting to something like the manual callback mechanism in 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'))
Simplified logging from my real code: <url letter> <page number or _ if no more pages> in order of requesting:
A 1
A _
B 1
C 1
D 1
E 1
F 1
E _
D _
B 2
C _
F 2
B 3
F _
B _
英文:
I am using an asynchronous JavaScript http client to download & process some paged JSON data from an initial URI & subsequent page URIs.
Each JSON page includes the URI for the next page, if there are more pages.
Sometimes, I need to process all pages for an initial URI & its subsequent page URIs before progressing to the next completely new initial URI.
I'd prefer to use await calls to the async http client to handle the initial URI & all subsequent page URIs all within an single call to my own async function (named f()), calling .then() on the Promise returned from f() to process a second initial URI only after the first initial URI & all of its pages have been downloaded & processed, rather than having to pass a callback to f() that I must manually call at the end of f().
e.g., can f() and/or the code that uses it below be modified such that 'uri2' will only be processed after all pages of 'uri1' have been processed without resorting to something like the manual callback mechanism in 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', () => {}))
Simplified logging from my real code: <url letter> <page number or _ if no more pages> in order of requesting:
A 1
A _
B 1
C 1
D 1
E 1
F 1
E _
D _
B 2
C _
F 2
B 3
F _
B _
答案1
得分: 1
Your first solution should work as-is.
任何 async 函数默认返回一个 Promise。您可以像在您的第一个代码块中对 f 执行 .then 一样对 async 函数的结果执行 .then。
另外,您可以在 async 函数上执行 await,以等待 Promise 解决后再继续。这正是您在调用 getObjectFromJsonFromUri 时所做的。
快速演示:
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; // 模拟每个 uri 有 2 页
let nextPageUri = initialPageUri
while (nextPageUri) {
const output = await getObjectFromJsonFromUri(nextPageUri);
// 处理 output
nextPageUri = output.nextPageUri
}
}
f('uri1').then(() => f('uri2'));
(请注意:我已经翻译了代码块,不包括注释部分。)
英文:
Your first solution should work as-is.
Any async function returns a Promise by default. You can do .then on the result of the async function as you do in your first block of code with f.
Additionally, you can perform await on async functions to wait for the Promise to resolve before continuing. That is exactly what you do in your call to getObjectFromJsonFromUri.
Quick demo:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
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'));
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论