如何在自定义类中使用pdf.js的promise?this指向undefined报错如何解决
错误原因
你遇到的TypeError: this is undefined错误本质是普通函数的this指向丢失,出在PdfLoader类的getPageAsCanvas方法的Promise回调逻辑中:
- 你使用
function(){}定义Promise回调函数时,函数内部的this不会绑定到PdfLoader实例,类内部默认开启严格模式,此时this指向为undefined - 错误具体触发点是最内层的
renderTask.promise.then(function(){ this.customPdfViewer.insertPageToBuffer(...) }),this找不到抛出异常后被外层的catch捕获,所以看起来单次调用中then和catch会先后执行,实际是内层then抛出异常触发了外层catch。
另外还存在一个隐性问题:每次调用getPageAsCanvas都会重新执行pdfjsLib.getDocument加载一次PDF,重复加载会浪费性能。
修复方案
两种方案任选其一即可解决问题:
方案1:将所有普通回调函数替换为箭头函数
箭头函数不会创建独立的this上下文,会继承外层作用域的this指向,直接替换所有function(){}格式的回调即可。
修改后的getPageAsCanvas代码如下:
getPageAsCanvas(pageNum){ // 新增PDF实例缓存逻辑,避免重复加载 if(this.pdfDocument) { this.renderPage(pageNum, this.pdfDocument); return; } this.loadingTask = pdfjsLib.getDocument(this.pdfPath); this.loadingTask.promise .then((pdfDocument) => { // 缓存PDF实例 this.pdfDocument = pdfDocument; if(pdfDocument.numPages >= pageNum){ return pdfDocument.getPage(pageNum).then((pdfPage) => { const viewport = pdfPage.getViewport({ scale: 1.0 }); const canvas = document.getElementById("pdfCanvas"); canvas.width = viewport.width; canvas.height = viewport.height; const ctx = canvas.getContext("2d"); const renderTask = pdfPage.render({ canvasContext: ctx, viewport, }); return renderTask.promise.then(() => { this.customPdfViewer.insertPageToBuffer(pageNum, canvas); }) }) } console.log('Page ' + pageNum + ' 不存在!'); }) .catch(function (reason) { console.error("Error: " + reason); }); }
方案2:提前缓存this指向
如果不想使用箭头函数,可以在方法开头把PdfLoader实例的this存到临时变量中,回调里直接使用该变量访问实例:
getPageAsCanvas(pageNum){ const _this = this; // 提前缓存实例this this.loadingTask = pdfjsLib.getDocument(this.pdfPath); this.loadingTask.promise .then(function (pdfDocument) { if(pdfDocument.numPages >= pageNum){ return pdfDocument.getPage(pageNum).then(function (pdfPage) { const viewport = pdfPage.getViewport({ scale: 1.0 }); const canvas = document.getElementById("pdfCanvas"); canvas.width = viewport.width; canvas.height = viewport.height; const ctx = canvas.getContext("2d"); const renderTask = pdfPage.render({ canvasContext: ctx, viewport, }); return renderTask.promise.then(function(){ _this.customPdfViewer.insertPageToBuffer(pageNum, canvas); }) }) } console.log('Page ' + pageNum + ' 不存在!'); }) .catch(function (reason) { console.error("Error: " + reason); }); }
额外优化建议
- 复用PDF实例:将加载完成的
pdfDocument缓存到PdfLoader实例上,后续加载页面时直接调用缓存实例的getPage方法,无需重复请求PDF文件 - 画布清空:如果复用单个canvas渲染多页,渲染前要手动清空画布,避免上一页内容残留
- 并发控制:多页批量加载时可以设置并发上限,避免同时渲染太多页面导致页面卡顿
内容的提问来源于stack exchange,提问作者Patrik Tschersich
相关产品推荐
相关产品推荐

