【问题标题】:Return a promise result in order using the library dom-to-image使用库 dom-to-image 按顺序返回 promise 结果
【发布时间】:2022-02-04 12:23:46
【问题描述】:

使用dom-to-image library,我们可以使用 Promises 转换图像。

使用示例,从DOM对象到PNG图片的转换:

var node = document.getElementById('my-node');

domtoimage.toPng(node)
    .then(function (dataUrl) {
        var img = new Image();
        img.src = dataUrl;
        document.body.appendChild(img);
    })
    .catch(function (error) {
        console.error('oops, something went wrong!', error);
    });

在幕后,toPng 方法是一个 Promise 对象的链:

/**
 * @param {Node} node - The DOM Node object to render
 * @param {Object} options - Rendering options, @see {@link toSvg}
 * @return {Promise} - A promise that is fulfilled with a PNG image data URL
 * */
function toPng(node, options) {
    return draw(node, options || {})
        .then(function (canvas) {
            return canvas.toDataURL();
        });
}

我想根据 Node 数组转换图像,然后按顺序获取这些图像。问题是当我调用toPng() 时,异步处理时间随 DOM 目标复杂性的函数而变化。所以它会在复杂度较低的页面之前返回,而不是按顺序返回页面。

let pages = document.querySelectorAll('.page');

pages.forEach(page => {
  let dataUrl = convertToPng(page.firstElementChild);
});

function convertToPng(page) {
  domtoimage.toPng(page)
    .then(function (dataUrl) {
      console.log(page); // prints pages randomly (e.g. page3, page1, page2) 
    })
}

一个可能的解决方案是使用 Promise.all() 方法并使用 forEach() 方法附加所有 Promise。但在这种情况下,我使用的是外部库,我不知道如何处理才能首先获取 page1,然后是 page 2,依此类推...

【问题讨论】:

  • 链式承诺?我假设您想等到 page1 “完成”后再开始 page2 等?
  • @JaromandaX 是的,我想等到 page1 完成,然后再开始下一页。
  • 如果你想变得更时髦,试试async / await
  • @Keith 我知道async / await,但我不知道如何在这个库中应用它。
  • ps.. 还要确保你从convertToPng返回你的承诺...

标签: javascript promise


【解决方案1】:

据我了解,您可以使用Promise.all。像这样的东西:

let pages = document.querySelectorAll('.page');

Promise.all(Array.prototype.slice.call(pages,0)
.map(page =>domtoimage.toPng(page))
).then(pages=>{.... your handler....});

有关 Promise.all 的更多信息,您可以从 MDN 获得

【讨论】:

  • 看到 OP 显然在使用现代 JS - 为什么不 Promise.all(Array.from(pages, page =>domtoimage.toPng(page))).then(pages => { ... }) :p
  • 可能我有点正统:)
  • 感谢您的回复,但我不明白如果querySelectorAll 返回一个数组对象,为什么要使用array.slicearray.from。 @JaromandaX。编辑:好的,它是NodeList,我一直认为它与数组相同,但它只继承forEach 方法。
  • 它不返回一个数组 .. 它是一个 NodeList ... 具有有限的类似数组的功能(即它有一个length 属性,在一些现代浏览器中是一个.forEach.keys.values.entries 方法......但没有 .map 方法 - 这对于呈现的代码工作至关重要)
  • 我使用了这个解决方案。但是,无论我是否颠倒它,数组项的顺序总是错误的。如何解决顺序问题?
【解决方案2】:

使用async/await

HTML:

<div class="page">Hello 1</div>
<div class="page">Hello 2</div>
<div class="page">Hello 3</div>
<div class="page">Hello 4</div>

JS (JSFiddle Link):

let pages = document.querySelectorAll('.page');

async function getPageUrls() {
    for (let page of pages) {
        const url = await convertToPng(page);
        console.log(url);
    }
}

async function convertToPng(page) {
    return await domtoimage.toPng(page);
}

getPageUrls();

基于 OP 示例 (JSFiddle):

let pages = document.querySelectorAll('.page');

for (let page of pages) {
    convertToPng(page);
}

async function convertToPng(page) {
    const dataUrl = await domtoimage.toPng(page);
    console.log(dataUrl);
}

getPageUrls();

使用此解决方案,代码执行将停止,直到convertToPng 在每次迭代中产生结果。

【讨论】:

  • 视为您使用async / await,摆脱那个thenable.. :)
  • @Keith 你完全正确!更新示例代码,谢谢!
  • 几乎.. :),.. async function convertToPng(page) { const dataUrl = await domtoimage.toPng(page); console.log(page); } 如果正在复制 OP.. 当然他还没有使用 dataUrl,但有人认为他也会想要。他甚至可能想返回...... atm。他没有。我知道很奇怪.. :)
  • 啊,我明白你的意思了。我试图通过返回结果而不是在函数中将其注销来使其更具可重用性,尽管如此:)
【解决方案3】:

链接promise以使其按顺序运行的最简单方法是使用Array#reduce

pages.reduce((promise, page) => 
    promise.then(() => convertToPng(page.firstElementChild)),
    Promise.resolve() // this starts the chain
);

正如你在评论wait until page1 is complete, before beginning with the next page中所说的那样

不过,我觉得只有输出顺序很重要,在这种情况下,@VasylMoskalov 的答案更合适

正如@Keith 在评论中指出的那样——convertToPng 不返回任何东西(更不用说承诺了)

function convertToPng(page) {
//vvvvvv important you return the promise here
  return domtoimage.toPng(page)
    .then(function (dataUrl) {
      console.log(page); // prints pages randomly (e.g. page3, page1, page2) 
    })
}

【讨论】:

  • 你能更详细地解释一下数组会做什么吗?我了解reduce method,但我不知道它为什么以及如何工作。谢谢
  • 据我所知 Promise.all 保留顺序
  • 这创建了一个承诺链......虽然我没有展示一件事,所以可能会清除它
  • @VasylMoskalov - 是的,由 Promise.all 解析的数组将输入数组的顺序保留到 Promise.all - 我认为你的答案可能更合适,因为异步的东西已经完成“作为尽可能快”,就像这段代码一样,它一次完成一个
  • 从 OP 所说的来看,你的是正确的.. -> yes I want to wait until page1 is complete,
猜你喜欢
  • 1970-01-01
  • 2021-08-11
  • 2018-08-29
  • 1970-01-01
  • 2015-12-05
  • 2013-01-19
  • 2021-07-23
  • 2013-09-26
  • 1970-01-01
相关资源
最近更新 更多