Promise from loadDocument never resolving nor rejecting

WebViewer Version: 10.9.0

Do you have an issue with a specific file(s)? No
Can you reproduce using one of our samples or online demos? Yes
Are you using the WebViewer server? No
Does the issue only happen on certain browsers? No
Is your issue related to a front-end framework? Yes
Is your issue related to annotations? No

Please give a brief summary of your issue:
(Think of this as an email subject)

This is less of a troubleshooting related question. More of a request for some extra clarification about the expected behavior of loadDocument.

Please describe your issue and provide steps to reproduce it:
(The more descriptive your answer, the faster we are able to help you)

What is the expected behavior of calling loadDocument a second time, before the promise from the first is resolved or rejected? So let’s say we call it with a file (e.g. a Blob). But before it even gets a chance to emit the beforeDocumentLoaded event, we call loadDocument again with a different file. Does it handle closing the previous file automatically and run through loading the new file from the start? Do I need to explicitly call closeDocument before calling loadDocument again? If so, should we also call dispose?

The reason for asking this is that we’re experiencing loadDocument not resolving after the second call.

// In a useMemo
const viewer = Core.DocumentViewer();

// ... later in a useEffect
await viewer.loadDocument(file, opts); // <-- This never resolves or rejects, and appears "stuck" 

The reason it’s being called again so quickly is because we wrapped Core.DocumentViewer in our own React component for our own UI, and multiple changes on various states in its parent component are causing multiple re-renders, leading to this method eventually getting called multiple times. Any ideas why that promise would never resolve in this case?

Now, we already understand that our React components need to be much improved to avoid re-rendering so often in a short period of time. That’s something we’re already working on. But, while not ideal, I would still like to get a better understanding of what’s happening when loadDocument is called like this while a previous call is in flight. Hopefully leading to some insight to the problem of the promise just refusing to resolve, seemingly pausing our app indefinitely.

Please provide a link to a minimal sample where the issue is reproducible: Not applicable.

1 Like

Hello James,

Thank you for contacting us.

Are there any console errors? Could you provide a screenshot? The loadDocument API should handle this case as mentioned in our guide.

Does calling closeDocument or dispose change the behavior in your application? Additionally, is there a reason you need to call loadDocument multiple times quickly? Perhaps, you could add some logic to wait for the document to complete loading before calling it again.

Best Regards,
Darian

1 Like

Hey Darian. Thanks for the reference to that doc. So it looks like that’s telling me webviewer should close the document automatically within subsequent calls to loadDocument. Having tried calling closeDocument and dispose explicitly, it does not appear to change behavior.

There are no console errors. The app is just stuck at the call to loadDocument waiting for the promise to either resolve or reject.

Additionally, is there a reason you need to call loadDocument multiple times quickly?

Well, there’s no need to call it multiple times quickly. As I mentioned in my initial post, the multiple calls are due to how our React component (and its parent component) re-renders from various state changes. We are actively trying to improve that to limit the number of re-renders. But In the meantime, I’m looking to get a better understanding of the current behavior of loadDocument in hopes that we can better handle this situation regardless.

Perhaps, you could add some logic to wait for the document to complete loading before calling it again.

Indeed, we are trying out debouncing the calls to loadDocument. Hopefully, this will at least be a good short term solution. But if there are no other “official” ways to deal with multiple calls like this then we may just stick with this approach indefinitely.

Sounds like this is technically unexpected according to your current documentation. Perhaps uncommon, but maybe it’s an edge case your team may want to look into. Otherwise, add a statement in your documentation that multiple calls to loadDocument before promises are resolved is undefined behavior.

1 Like

Hello James,

Thank you for the response. Would it be possible for you to provide a minimal sample project to reproduce the issue? This would allow us to investigate the issue more easily. We have a basic React project you can use as a template: GitHub - ApryseSDK/webviewer-react-sample: Sample to demonstrate integrating WebViewer into React

1 Like

Yea I totally understand that a minimal sample project will be of more help. I’ll try to find some time late week, or maybe next week to reproduce it with that template.

In the meantime, we’ve found a workaround that appears to address the multiple re-renders, no longer “pausing” our app. Thanks for your help so far :pray:

1 Like

I think I got a good repeatable example that closely mimics what happens in our app. This is based on your custom UI template instead (as opposed to the webviewer-react-sample).

Only change I made was to src/App.js so I’ll just post that here:

import React, { useRef, useEffect } from "react";
import "./App.css";

const App = () => {
  const viewer = useRef(null);
  const scrollView = useRef(null);

  useEffect(() => {
    const Core = window.Core;
    Core.setWorkerPath("/webviewer");
    Core.enableFullPDF();

    const documentViewer = new Core.DocumentViewer();
    documentViewer.setScrollViewElement(scrollView.current);
    documentViewer.setViewerElement(viewer.current);
    documentViewer.enableAnnotations();

    const opts = {
      fallbackToClientSide: true,
      officeOptions: {
        formatOptions: { displayChangeTracking: true },
      },
      extension: "docx",
    };

    console.log("[1] loading document");
    documentViewer
      .loadDocument("/files/hello-pdftron.docx", opts)
      .then(() => {
        console.log("[1] loaded document");
      })
      .catch((err) => {
        console.log("[1]", err);
      });

    setTimeout(() => {
      console.log("[2] loading document");
      documentViewer
        .loadDocument("/files/hello-apryse.docx", opts)
        .then(() => {
          console.log("[2] loaded document");
        })
        .catch((err) => {
          console.log("[2]", err);
        });
    }, 200);

    const onDocumentLoaded = () => {
      console.log("[event] documentLoaded");
    };

    documentViewer.addEventListener("documentLoaded", onDocumentLoaded);

    return () => {
      documentViewer.removeEventListener("documentLoaded", onDocumentLoaded);
    };
  }, []);

  return (
    <div className="App">
      <div id="main-column">
        <div className="flexbox-container" id="scroll-view" ref={scrollView}>
          <div id="viewer" ref={viewer}></div>
        </div>
      </div>
    </div>
  );
};

export default App;

To mimic the “re-render” problem I mentioned in my earlier post, this calls loadDocument again shortly after the first call. Notice the delay of 200ms for setTimeout. This consistently ends up showing the following in the console, seemingly never resolving or rejecting the promise.

If you update that delay to something much higher (e.g. 2000) then both calls eventually succeed, rendering the files in the viewer one after the other. We see all the expected logs.

I tested this in both Firefox and Chrome and I’m observing the same exact behavior in both. Attached are the two example docx files I created for this example. Just place them in the /public/files directory.

hello-apryse.docx (12.8 KB)
hello-pdftron.docx (12.8 KB)

Hope this is helpful.

1 Like

Hello James,

Thank you for the response. We will look into this and respond when we have an update.

1 Like