dotNET
dotNET

Reputation: 35410

async function re-entering without being called

This is most probably due to my limited experience with JavaScript, but I haven't been able to figure this out. This is a Vue 2 + Typescript project, but the specific problem doesn't have anything to do with Vue or Typescript.

OK. So I have added the following functions in one of my components. Primary purpose of this component is to take a PDF file and return the rasterized version of its first page in the form of a Blob. Here are my functions:

async importButtonHandler () {
  if (this.file) {
    const blob = await this.pdfToBlob(this.file)
    if (blob) {
      const file2 = new File([blob], 'raster_pdf', { type: 'image/png' })
      // Further processing
    }
  }
}

async pdfToBlob (pdfFile: File): Promise<Blob|null> {
  const canvas: HTMLCanvasElement = document.createElement('canvas')
  const buffer = await this.readFileAsync(pdfFile)
  const typedarray = new Uint8Array(buffer)

  // get handle of pdf document
  const doc = await pdfjsLib.getDocument(typedarray).promise

  // get handle of page
  const pdfpage = await doc.getPage(1)

  // get viewport to render the page at required scale
  const viewport = pdfpage.getViewport()

  // set canvas height same as viewport height
  canvas.height = viewport.height
  canvas.width = viewport.width

  const renderContext: RenderParameters = {
    canvasContext: canvas.getContext('2d') as CanvasRenderingContext2D,
    viewport: viewport
  }

  // render the page contents in the canvas
  await pdfpage.render(renderContext).promise

  return new Promise((resolve, reject) => {
    try {
      canvas.toBlob(resolve)
    } catch (error) {
      reject(error)
    }
  })
}

readFileAsync (file: File): Promise<ArrayBuffer> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()

    reader.onload = () => {
      resolve(reader.result as ArrayBuffer)
    }

    reader.onerror = reject

    reader.readAsArrayBuffer(file)
  })
}

It hits the function pdfToBlob and calls readFileAsync from therein, reads the file into buffer correctly, but immediately after that (on const typedarray = new Uint8Array(buffer)) instead of executing that line, it hits pdfToBlob function again and then throws an exception saying:

TypeError: Cannot read properties of undefined (reading 'getDocument')

and for the life of me I can't figure out why it enters the function again (there is no other place where this function is called).

My gut feeling is that it is about the Promise that I'm returning in the function, but all my experimentation hasn't led me to any clue.

Can someone help me figure out what's wrong here?

Upvotes: 1

Views: 362

Answers (1)

dotNET
dotNET

Reputation: 35410

Figured it out (kind of).

Firstly, my pdfjsLib needed to be imported correctly and supplied with a worker before calling getDocument. This was the cause of my second issue. Here is the correct way of importing the lib and worker in Vue 2/Typescript project:

import * as pdfjsLib from 'pdfjs-dist'
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'

Then supply worker to the library and call getDocument:

pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker
const doc = await pdfjsLib.getDocument(YOUR_PDF_DATA).promise

Rest of the code in my OP works correctly. Note that pdf.worker.entry doesn't have type definitions (or at least I couldn't find them anywhere), but it works nonetheless.

The first issue of getting handler called multiple times seems to be a known issue (or behavior if you like) in Vuetify. They suggest to use native, or prevent or stop when defining the click attribute (like @click.stop="importButtonHandler") to mitigate the behavior. I have tried it without success. My handler is still called twice. In my particular scenario this doesn't cause any functional harm so I can live with it for now, but I can think of situations where this behavior could be disastrous.

Hope this helps someone down the road.

Upvotes: 1

Related Questions