考虑 Resolve.all 链中的最后一个承诺

问题描述 投票:0回答:1

我想从 Confluence 页面导出 HTML 内容。这些可以包含带有

<img>
属性的
src
标签,这些属性只是普通的超链接。因为我也想导出这些内容,所以我决定将
src
内容替换为相应的数据 URL,这样就有
src="data:image/png;base64,AEFJEFEF…"

这当然需要通过 HTTP 获取图像,而且这只能以异步方式完成。此外,它还包含大量“嵌套”异步调用。

这是我到目前为止的代码:

    /**
     * @param {HTMLTableCellElement | undefined} cell
     */
    async #getCellHtml(cell) {
        if (!cell) return undefined;

        const srcMap = {}

        for await (const imgElement of cell.querySelectorAll('img')) {
            if ("attachment" !== imgElement.dataset.linkedResourceType) {
                return;
            }
            const imgUrl =
                new URL(imgElement.src, imgElement.dataset.baseUrl);
            await fetch(imgUrl)
                .then(response => response.blob())
                .then(blob => blob.arrayBuffer())
                .then(arrayBuffer => {
                    srcMap[imgElement.src] =
                        `data:${imgElement.dataset.linkedResourceContentType};base64,`
                        + Buffer.from(arrayBuffer).toString('base64');
                });
        }

        const cellHtml = cell.innerHTML;

        Object.entries(srcMap).forEach(([imgSrc, dataUrl]) => {
            cellHtml.replace(imgSrc, dataUrl)
        })

        return cellHtml;
    }

作为参考,此类 HTML 如下所示:

<p style="text-align: left;"><br/></p>
<p style="text-align: left;"><span
        class="confluence-embedded-file-wrapper confluence-embedded-manual-size"><img
        class="confluence-embedded-image" draggable="false" width="639"
        src="/confluence/download/attachments/2345432345/image-2024-7-11_16-48-22-1.png?version=1&amp;modificationDate=1720709302000&amp;api=v2"
        data-image-src="/confluence/download/attachments/235432345/image-2024-7-11_16-48-22-1.png?version=1&amp;modificationDate=1720709302000&amp;api=v2"
        data-unresolved-comment-count="0" data-linked-resource-id="345654345"
        data-linked-resource-version="1" data-linked-resource-type="attachment"
        data-linked-resource-default-alias="image-2024-7-11_16-48-22-1.png"
        data-base-url="https://suite.acme.com/confluence"
        data-linked-resource-content-type="image/png"
        data-linked-resource-container-id="1491043790"
        data-linked-resource-container-version="1" alt=""/></span></p>
<p style="text-align: left;"><br/></p>
<p style="text-align: left;"><br/></p>

我的目的是循环遍历所有

<img>
元素,找到相关的
<img>
标签,获取它们的图像数据,并收集替换数组。之后,我只需将所有发现替换为各自的数据 URL。

我想我想要的是这样的:

cell.querySelectorAll('img').map(cell => {
  // return a Promise that combines all the fetching etc.
  // so that it resolves() with returning the base64 string(!).
  return new Promise()…
});

在我

map()
将此数组添加到 Promises 之后,我可以
Promise.all()
然后替换 HTML。

在所有其他承诺都已履行后,我不知道如何“回报”最后一个承诺。我的代码是否应该使用

await
而不是
.then()
调用,这样我就不会进入回调上下文?

javascript async-await promise
1个回答
0
投票

使用await而不是链接

.then()
调用:

/**
 * @param {HTMLTableCellElement | undefined} cell
 */
async function getCellHtml(cell) {
    if (!cell) return undefined;

    const imgElements = Array.from(cell.querySelectorAll('img[data-linked-resource-type="attachment"]'));
    
    const srcMapPromises = imgElements.map(async (imgElement) => {
        const imgUrl = new URL(imgElement.src, imgElement.dataset.baseUrl);
        const response = await fetch(imgUrl);
        const blob = await response.blob();
        const arrayBuffer = await blob.arrayBuffer();
        const base64String = Buffer.from(arrayBuffer).toString('base64');
        
        return {
            originalSrc: imgElement.src,
            dataUrl: `data:${imgElement.dataset.linkedResourceContentType};base64,${base64String}`
        };
    });

    const srcMap = await Promise.all(srcMapPromises);
    
    let cellHtml = cell.innerHTML;

    srcMap.forEach(({ originalSrc, dataUrl }) => {
        cellHtml = cellHtml.replace(new RegExp(originalSrc, 'g'), dataUrl);
    });

    return cellHtml;
}

我首先使用

Array.from()
收集 HTML 单元格元素中的所有图像元素,将
querySelectorAll
返回的 NodeList 转换为数组以使用
map()
。在
map()
回调中,我使用
async
/
await
来获取图像,将 blob 转换为数组缓冲区,然后将该缓冲区转换为 base64 字符串。回调返回一个包含原始 src 和新数据 URL 的对象。

通过使用

Promise.all()
,我确保在继续之前完成所有获取操作。

然后,我使用

Promise.all()
的结果来替换 HTML 内容中的图像 src 属性。此方法可确保正确处理所有异步操作,并且仅在获取并转换所有图像后才更新最终 HTML 内容。要使用此函数,请使用适当的 HTML 单元格元素调用它:

const cell = document.querySelector('your-cell-selector');
getCellHtml(cell).then((htmlContent) => {
    console.log(htmlContent);
});
© www.soinside.com 2019 - 2024. All rights reserved.