Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimization: Caching library #2337

Closed
YaromichSergey opened this issue May 17, 2024 · 26 comments
Closed

Optimization: Caching library #2337

YaromichSergey opened this issue May 17, 2024 · 26 comments
Assignees
Labels
21 nuisance a bug that only shows in the logs; or: a bug with an obvious work-around every user finds quickly Solved

Comments

@YaromichSergey
Copy link

YaromichSergey commented May 17, 2024

I have a question-request. How to cache/optimize library sources and loads. We use pdf library to view pdf. We have component which re-initialize a lot of times during user session. And every time we need to load js files. How to optimize this process? Is it possible to load only first time? We understand that js files caching and next time it's load from browser cache. But we still need time to process js. What is your recommendation for best optimization, reusabillity component? Thanks in advance

@stephanrauh stephanrauh self-assigned this May 22, 2024
@stephanrauh stephanrauh added the user support You've got a question, or a misconfiguration, or simply need a hint how to get things up and running label May 22, 2024
@stephanrauh
Copy link
Owner

stephanrauh commented May 22, 2024

pdf.js is removed from memory when you destroy the Angular component. Instead, you can simply hide the <ngx-extended-pdf-viewer> component. You just have to make sure the component is visible before modifying the [src] attribute. Maybe you need to add a timeout like so:

this.showPDF = true;
setTimeout(() => this.src="/assets/my-favorite-pdf-file.pdf", 0);
<ngx-extended-pdf-viewer [style.hidden]="!showPDF" [src]="src">

Note that this is pseudo-code - I didn't try it. If it works, please tell me, so I can fix the errors of my pseudo-code for the benefit of other users.

@YaromichSergey
Copy link
Author

It's not what we are looking for. We need something global in case of saving viewer. I'm trying to initialize it with ComponentFactoryResolver to save instance of library but it still re-launch(pdf-worker) and others and it's a bit harder to use. Main problem to avoid load library(from source from cache what ever) and avoid executing JS each time it need. Best behavior: load library(all source what needs), cache them and each time we invoke it be ready to show document as it waits for them. We need to understand if it possible. By our investigating it looks like not, at least with how we using library.

@stephanrauh
Copy link
Owner

There are checks preventing loading the viewer-*.js and the pdf-*.js twice. The check regarding pdf-*.js seems to be broken, but at least the viewer shouldn't be loaded twice. However, when I tested it, none of these file were loaded twice. Can you confirm that?

Remains the worker. That's probably the most annoying file because it's so huge. After reading the api.js file I believe it's possible to cache the worker, but I didn't manage to wrap my head around it yet.

How urgent is your issue? Are you ready to dive into the source code yourself? At the moment, I'm trying to catch up after my vacations, so I appreciate every help I can get!

@stephanrauh stephanrauh added nuisance a bug that only shows in the logs; or: a bug with an obvious work-around every user finds quickly and removed Solved labels May 23, 2024
@YaromichSergey
Copy link
Author

We are really interesting in it. Unfortunately I can't help with it. I tested again and can see that viewer-*.js and pdf-.js loads only first time. We are waiting for caching workers(pdf.worker-) and all you can do as soon as you can implement it. If you can mention in some way with ready state of this it will be great. Thank you in advance

@stephanrauh
Copy link
Owner

Issue #2329 is doing the exact opposite: they noticed I've implemented a memory leak, so the garbage collector can't remove the JavaScript resources. I'll tackle #2329 first. It might make your issue worse.

Here's something you can try: you can load the JavaScript files yourself. Actually you can even load the worker file yourself, but that means the worker is executed in the main thread, so it reduces the rendering performance. It depends on the use-case which tradeoff is worse.

  private loadViewer(): void {
    globalThis['ngxZone'] = this.ngZone;
    this.ngZone.runOutsideAngular(() => {
      this.needsES5().then((needsES5) => {
        const viewerPath = this.getPdfJsPath('viewer', needsES5);
        const script = this.createScriptElement(viewerPath);
        document.getElementsByTagName('head')[0].appendChild(script);
      });
    });
  }

  private loadPdfJs() {
    globalThis['ngxZone'] = this.ngZone;
    this.ngZone.runOutsideAngular(() => {
      if (!globalThis['pdfjs-dist/build/pdf']) {
        this.needsES5().then((needsES5) => {
          if (needsES5) {
            if (!pdfDefaultOptions.needsES5) {
              console.log(
                "If you see the error message \"expected expression, got '='\" above: you can safely ignore it as long as you know what you're doing. It means your browser is out-of-date. Please update your browser to benefit from the latest security updates and to enjoy a faster PDF viewer."
              );
            }
            pdfDefaultOptions.needsES5 = true;
            console.log('Using the ES5 version of the PDF viewer. Your PDF files show faster if you update your browser.');
          }
          if (this.minifiedJSLibraries && !needsES5) {
            if (!pdfDefaultOptions.workerSrc().endsWith('.min.mjs')) {
              const src = pdfDefaultOptions.workerSrc();
              pdfDefaultOptions.workerSrc = () => src.replace('.mjs', '.min.mjs');
            }
          }
          const pdfJsPath = this.getPdfJsPath('pdf', needsES5);
          if (pdfJsPath.endsWith('.mjs')) {
            const src = pdfDefaultOptions.workerSrc();
            if (src.endsWith('.js')) {
              pdfDefaultOptions.workerSrc = () => src.substring(0, src.length - 3) + '.mjs';
            }
          }
          const script = this.createScriptElement(pdfJsPath);
          script.onload = () => {
            if (!(globalThis as any).webViewerLoad) {
              this.loadViewer();
            }
          };
          document.getElementsByTagName('head')[0].appendChild(script);
        });
      } else if (!(globalThis as any).webViewerLoad) {
        this.loadViewer();
      }
    });
  }

  private createScriptElement(sourcePath: string): HTMLScriptElement {
    const script = document.createElement('script');
    script.async = true;
    script.type = sourcePath.endsWith('.mjs') ? 'module' : 'text/javascript';
    this.pdfCspPolicyService.addTrustedJavaScript(script, sourcePath);
    return script;
  }

In your application, you can simplify the code a lot. You don't need all the options a general-purpose library needs.

@stephanrauh
Copy link
Owner

I've got an idea how to improve performance in version 21. If everything goes according to plan, I'm going to move the code loading and managing the base library into a service, so it doesn't have to be loaded every time you navigate from page to page.

@YaromichSergey
Copy link
Author

It will be great. Looking forward for your news. Thanks for replying here. As for the error with old version of chrome with new version. Do you have a plan to fix it? Or just throw error with event?. Unfortunately we still have old users with old browsers and need to support them. For now we have workaround with window.onerror but we understand that it's not the best idea. Could you share you thoughts?

@stephanrauh
Copy link
Owner

Which bug are you referring to? Maybe Promise.withResolvers? I think you might be able to polyfill it - but to my surprise, I haven't found a polyfill yet.

@YaromichSergey
Copy link
Author

YaromichSergey commented Jun 28, 2024

Yes, about this bug. I tried to polyfill it and it's isn't working. I tried to add to angular code something like

function promiseWithResolvers() {
  let resolve;
  let reject;
  const promise = new Promise(
    (res, rej) => {
      // Executed synchronously!
      resolve = res;
      reject = rej;
    });
  return {promise, resolve, reject};
}

But it didn't work as expected(nothing changed). Don't know is it read code and can work(you can test it)

@stephanrauh
Copy link
Owner

That's pretty much the approach I'd use, just slightly different - maybe that's why it's not working?

I'd add these lines to the module file or to the constructor of the component using <ngx-extended-pdf-viewer>:

Promise.promiseWithResolvers() = function() {
  let resolve;
  let reject;
  const promise = new Promise(
    (res, rej) => {
      // Executed synchronously!
      resolve = res;
      reject = rej;
    });
  return {promise, resolve, reject};
}

@stephanrauh
Copy link
Owner

BTW, if everything fails, there's always the option to add the attribute [forceUsingLegacyES5]="true".

stephanrauh added a commit that referenced this issue Jun 29, 2024
…ice so multiple instances can use the JavaScript files without reloading them; move objects from `globalThis` to `PdfViewerApplication`; stop using `ngZone.runOutsideAngular()` to make it easier to use the viewer without zone.js
@stephanrauh
Copy link
Owner

Have a look at version 21.0.0-alpha.0. I'd be very surprised if it's not full of bugs - but it works surprisingly good, and jumping between the demos of the showcase seems to be considerably faster.

@YaromichSergey
Copy link
Author

I have already tested it and have this moments:

  1. assetsFolder stopped work(it add full passed string to current domain for example: http://localhost:4200, and as assetsFolder = 'http://localhost:4201/assets/pdf' in result will be - 'http://localhost:4200/http://localhost:4201/assets/pdf'. In previous version everything is ok.
  2. Could you delete logs pls(from console) or provide a way how to hide them?
  3. As for openning process - something changed with toolbar. After starting loading - shows more buttons(initial), after load - shows another ones(We are not using them at all and hide it, for now logic changed). If it part of the logic - it's ok, we will fix on our side.

As for loading process, it's hard to say that it loads faster, need to re-test it again after your fixes. Thanks in advance

stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 1, 2024
…ding the JavaScript files to a service so multiple instances can use the JavaScript files without reloading them; move objects from `globalThis` to `PdfViewerApplication`; stop using `ngZone.runOutsideAngular()` to make it easier to use the viewer without zone.js
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 1, 2024
…ding the JavaScript files to a service so multiple instances can use the JavaScript files without reloading them; move objects from `globalThis` to `PdfViewerApplication`; stop using `ngZone.runOutsideAngular()` to make it easier to use the viewer without zone.js
@stephanrauh
Copy link
Owner

Thanks for testing! This helps me a lot!

@stephanrauh
Copy link
Owner

stephanrauh commented Jul 1, 2024

To dos:

  • absolute assets folder (fixed with version 21.0.0-alpha.2)
  • log spam
  • all toolbar buttons are visible during startup
  • load the minified file (possible that's a bug of the showcase only)
  • fix two-way binding of forms
  • find out why the initial value of the forms isn't overridden by Angular in the demo when opening it the second time
  • fix wheel support when opening the PDF viewer the second time
  • printing with CTRL+P logs an error message when opening the second instance of the viewer
  • fix SSR (PdfSidebarContentComponent contains references to window, and there's an error message PdfRotatePageComponent.PDFViewerApplication is undefined)
  • when "highlight all" is activated, the current highlight can't be distinguished from the other matches
  • the first password demo of the showcase is broken

@stephanrauh stephanrauh removed the user support You've got a question, or a misconfiguration, or simply need a hint how to get things up and running label Jul 1, 2024
@stephanrauh
Copy link
Owner

stephanrauh commented Jul 1, 2024

I'm afraid I can't reproduce your toolbar bug. I've throttled network speed to 1 mbit/s, so loading too a long time, and ran the toolbar demo (the local counterpart of https://pdfviewer.net/extended-pdf-viewer/hiding-buttons). I didn't see any button that shouldn't be there. This is my screenshot, taken while the JavaScript files were still loading:
image

stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 1, 2024
…tivated or deactivated in an existing viewer when showing another document
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…rom `globalThis` to `PdfViewerApplication`
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…dow` object; use a sensible default value for `filenameForDownload`; stop throwing errors when closing a dialog that's already closed (happens sometimes when `ngOnChanges` fires)
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…ails so they don't need to pollute the `window` object;
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…rom `globalThis` to `PdfViewerApplication`
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…dow` object; use a sensible default value for `filenameForDownload`; stop throwing errors when closing a dialog that's already closed (happens sometimes when `ngOnChanges` fires)
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…ails so they don't need to pollute the `window` object;
stephanrauh added a commit to stephanrauh/extended-pdf-viewer-showcase that referenced this issue Jul 14, 2024
stephanrauh added a commit to stephanrauh/extended-pdf-viewer-showcase that referenced this issue Jul 14, 2024
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…ails so they don't need to pollute the `window` object
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
stephanrauh added a commit to stephanrauh/pdf.js that referenced this issue Jul 14, 2024
…ails so they don't need to pollute the `window` object
stephanrauh added a commit that referenced this issue Jul 14, 2024
…interface `PasswordPrompt` (now it includes the `open` and `close` methods)
stephanrauh added a commit that referenced this issue Jul 15, 2024
…uishable from the other find results when `highlight all` is selected
@stephanrauh stephanrauh added Solved and removed Working on it I've started to work on this ticket. More often than not, that means the feature is delivered soon. labels Jul 15, 2024
@stephanrauh
Copy link
Owner

I believe version 21.0.0-alpha.9 delivers a pretty stable implementation of your feature.

Enjoy!
Stephan

@YaromichSergey
Copy link
Author

YaromichSergey commented Jul 16, 2024

2024-07-16 12-02-11.webm
Look at recording because it's hard to reproduce. It started not from the previous version but some of previous. When changing the zoom, sometimes it hardstack in some places(ctrl + wheel). After changing zoom it helps sometimes in case of scroll appear. Could help understand what is this problem, how to solve it? Output method like (currentZoomFactor) (zoomChange) stop work in this case(stop emmit a value). On your demo I can reproduce only for a few times and scroll enough for unblock it. Thanks in advance.

@stephanrauh
Copy link
Owner

Version 21.0.0-beta.1 ships with more improvements. Earlier versions accidentally loaded the worker file twice. Now everything should work as intended.

Maybe it's even possible to re-use the worker - but let's address this in a future ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
21 nuisance a bug that only shows in the logs; or: a bug with an obvious work-around every user finds quickly Solved
Projects
None yet
Development

No branches or pull requests

2 participants