Using the dom-to-image library we can convert an image using Promises.
Usage example, conversion from DOM object to PNG image:
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);
});
Behind the scenes, the toPng method is chain of a Promise object:
/**
* @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();
});
}
I want to convert images based on a Node array, and then, to get that images in order. The problem is when I call toPng()
the async process time varies in function of the DOM target plexity. So it will return before the pages with less plexity, instead of return the pages in order.
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)
})
}
A possible solution would be use a Promise.all()
method and attach with a forEach()
method all the Promises. But in this case, that I'm using an external library, I don't know how to approach in order to get first the page1, then the page 2, and so on...
Using the dom-to-image library we can convert an image using Promises.
Usage example, conversion from DOM object to PNG image:
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);
});
Behind the scenes, the toPng method is chain of a Promise object:
/**
* @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();
});
}
I want to convert images based on a Node array, and then, to get that images in order. The problem is when I call toPng()
the async process time varies in function of the DOM target plexity. So it will return before the pages with less plexity, instead of return the pages in order.
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)
})
}
A possible solution would be use a Promise.all()
method and attach with a forEach()
method all the Promises. But in this case, that I'm using an external library, I don't know how to approach in order to get first the page1, then the page 2, and so on...
async / await
– Keith
Commented
Feb 22, 2018 at 11:37
async / await
but I don't know how to apply it in this library.
– Angel Luis
Commented
Feb 22, 2018 at 11:41
convertToPng
...
– Keith
Commented
Feb 22, 2018 at 11:48
As I understand for this case you can use Promise.all
. Smthing like this one:
let pages = document.querySelectorAll('.page');
Promise.all(Array.prototype.slice.call(pages,0)
.map(page =>domtoimage.toPng(page))
).then(pages=>{.... your handler....});
More information about Promise.all you can get from MDN
Using 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();
Based off OPs sample (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();
With this solution code execution is stopped until convertToPng
yields a result at each iteration.
The easiest way to chain the promises so they run in sequence is using Array#reduce
pages.reduce((promise, page) =>
promise.then(() => convertToPng(page.firstElementChild)),
Promise.resolve() // this starts the chain
);
This will, as you stated in the ment wait until page1 is plete, before beginning with the next page
Though, I get the feeling it's only the output order that is important, in which case the answer by @VasylMoskalov is more appropriate
As @Keith pointed out in a ment - convertToPng does not return anything (let alone a promise)
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)
})
}