jdub jdub - 2 months ago 30
Javascript Question

PDFJS and promise reducing

So, it appears my understanding of js promises is quite lacking. I am working with PDFJS to display all pages of a pdf as a scrollable list of canvases (which is currently working). When i resize the window, i call a function like this (i have a

pages
array which has stored all the pdf pages from
pdf.getPage(index)
:

// PART A
// This part works, though "pages" is a representation obviously
function reRender() {
const pages = [
pdfjsPage0, // the result of pdf.getPage(0)
pdfjsPage1,
pdfjsPage2,
];

return pages.reduce((accum, page, index) => accum.then(() => {
const viewport = page.getViewport(scale);
const canvas = canvases[index];
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext('2d');
const renderContext = {
canvasContext: ctx,
viewport,
};
return page.render(renderContext);
}), Promise.resolve());
}


But, this code will not work as intended, as the promises are seemingly not returned as i'd expect:

// PART B
// This part only somewhat works, though "pages" is a representation obviously
// The promise will return and be resolved before all the pages have actually been rendered
// ie: reRender.then(//somecode), //somecode will execute before the pages have been rendered
function reRender() {
const pages = [
pdfjsPage0, // the result of pdf.getPage(0)
pdfjsPage1,
pdfjsPage2,
];

let promise = Promise.resolve(); //edited to add "()"

pages.forEach((page, index) => {
promise = promise.then(() => {
const viewport = page.getViewport(scale);
const canvas = canvases[index];
canvas.width = viewport.width;
canvas.height = viewport.height;
const ctx = canvas.getContext('2d');
const renderContext = {
canvasContext: ctx,
viewport,
};
return page.render(renderContext);
});
});

return promise;
}


However, when grabbing the pages, seemingly, the Promise needs to be built an entirely different way, as this works (the same type of buildup does not wait or seemingly go in the order i expect for re-rendering:

// PART C
// This works, nothing in the caller.then will execute until everything
// has resolved here
function getPagesAndAddCanvas(pdf, pages = [], canvas = []) {
let promise = Promise.resolve();

for (let i = 1; i <= pdf.numPages; i++) {
promise = promise.then(() => pdf.getPage(i)
.then(page => {
pages.push(page);
const canvas = angular.element('<canvas></canvas>');
containerElement.append(canvas);
canvases.push(canvas[0]);
})
)
}
return promise;
}


However, what should seemingly work in my mind, since PDFJS.getPage returns a promise, this somehow doesn't work and makes me abundantly confused about my understanding of promises:

// PART D
// This does not work at all, pdf.getPage in this scenario
// does not seem to ever return
function getPagesAndAddCanvas(pdf, pages = [], canvas = []) {
const pageLength = Array(pdf.numPages).fill(1);

return pageLength.reduce((accum, irrelevant, index) => accum.then(() => {
console.log(pdf);
console.log(`index: ${index}`); // this is called for index 0
return pdf.getPage(index)
.then(page => {
console.log(page); // this is NEVER called
pages.push(page);
const canvas = angular.element('<canvas></canvas>');
containerElement.append(canvas);
canvases.push(canvas[0]);
return Promise.resolve(); //edited to fix spelling
});
}), Promise.resolve());
}


Does anyone have insight on to how/why these seemingly contradict each other since PART A and PART C work, but PART B and PART D do not? I'm hitting a wall here and losing my wits.

Answer

Part B does not work because

let promise = Promise.resolve;

should be

let promise = Promise.resolve();

(but just use Part A which works and is cleaner).


Part D does not work because

          return Promise.resovle();

is a typo and because, in contrast to Part C, the index loops from 0 to numPages-1 unlike i that runs from 1 to numPages.

You really should add some .catch() handlers that log errors, and take a look at the console for uncaught exceptions.