javascript 调用Promise.all()会正确执行,但对单个承诺调用wait则不会

k2fxgqgv  于 2022-12-17  发布在  Java
关注(0)|答案(2)|浏览(126)

我有一些代码可以迭代表示文件的对象列表。在循环中有一个对“getRequest()”的调用,它返回类型“Promise”,然后是对“.text()”的调用,它返回“Promise”。我有一个工作解决方案,首先将所有的Promise放入列表中,然后调用Promise.all(),文档就被正确获取了。

但是,当我尝试另一个解决方案时,我直接等待promise返回,然后使用这些值,文档没有正确填充。

//----------------------------- WORKING SOLUTION ---------------------------------------
async function parseDocumentation(config: Map<string, ConfigElement[]>) {
    const promises: Promise<Response>[] = [];
    const documents: Documentation[] = [];

    config.forEach((sectionDef: ConfigElement[]) => {
        sectionDef.forEach(entry => {
            if (!entry.subsection) {
                // getRequest: (endpointUrl: string) => Promise<Response>
                promises.push(getRequest(entry.source));
            }
        });
    });
    try {
        await Promise.all(promises).then(async responses => {
            for (const response of responses) {
                if (!response.ok) {
                    return Promise.reject();
                } else {
                    const document = {source: '', markDown: '' };
                    document.markDown = await entry.text(); // Body.text(): Promise<string>
                    document.source = response.url;
                    documents.push(document);
                }
            }
        });
    } catch (e) {
        Dialog.error('Failed to load page.', e);
    }
}

//----------------------------- NON WORKING SOLUTION ---------------------------------------
async function parseDocumentation(config: Map<string, ConfigElement[]>) {
    const documents: Documentation[] = [];

    config.forEach((sectionDef: ConfigElement[], sectionName: string) => {
        sectionDef.forEach(async entry => {
            if (!entry.subsection) {
                entry.hash = 'some_hash_value_' + sectionName;
                await getRequest(entry.source).then(async response => {
                    if (!response.ok) {
                        return Promise.reject();
                    } else {
                        const document = {source: '', markDown: '' };
                        await response.text().then(async text => {
                            // would like to save entry.hash to document here
                            document.markDown = text;
                            document.source = response.url;
                            documents.push(document);
                        });
                    }
                });
            }
        });
    });
}

有人能解释一下发生了什么吗?我想我正确地反映了逻辑,但由于某种原因,第二个变体没有正确地填充文档。

yacmzcpb

yacmzcpb1#

问题出在.forEach(async ....构造上:回调是一个异步函数,其中的第一个await将返回/suspend * 该 * 回调函数(不是外部函数),使forEach循环继续下一次迭代。回调中的异步代码(await以下)仅在调用堆栈为空时执行,因此forEach在此之前已经完成了迭代。
要解决这个问题,使用一个不需要回调的循环结构,比如for..of。然后awaitparseDocumentation函数本身进行操作:

async function parseDocumentation(config: Map<string, ConfigElement[]>) {
    const documents: Documentation[] = [];

    for (const [sectionName: string, sectionDef: ConfigElement[]] of config) {
        for (const entry: ConfigElement of sectionDef) {
            // ...
        }
    }
}

问题不在于此,而是要尽量从await中获得所承诺的价值--避免then链:

async function parseDocumentation(config: Map<string, ConfigElement[]>) {
    const documents: Documentation[] = [];

    for (const [sectionName: string, sectionDef: ConfigElement[]] of config) {
        for (const entry: ConfigElement of sectionDef) {
            if (entry.subsection) continue;
            entry.hash = 'some_hash_value_' + sectionName;
            const response = await getRequest(entry.source);
            if (!response.ok) throw new Error("One of the responses was not OK");
            documents.push({
                source: response.url,
                markDown: await response.text(),
            });
        }
    }
}

请注意,在async函数中抛出错误将拒绝函数返回的承诺,原因是错误消息。

oxiaedzo

oxiaedzo2#

您应该:
let text = await response.text();

相关问题