OfficeDev / office-js

A repo and NPM package for Office.js, corresponding to a copy of what gets published to the official "evergreen" Office.js CDN, at https://appsforoffice.microsoft.com/lib/1/hosted/office.js.
https://learn.microsoft.com/javascript/api/overview
Other
686 stars 95 forks source link

Document paragraph listeners miss some events. #4874

Open JamieMair opened 2 months ago

JamieMair commented 2 months ago

Provide required information needed to triage your issue

Sometimes when the document is edited quickly (such as multiple copy and paste by the user), the events for paragraphs added/changed or removed are not executed at all.

Example code to reproduce:

const itemsChanged: string[] = [];
const itemsDeleted: string[] = [];

async function handleDeleted(event: Word.ParagraphDeletedEventArgs) {
  for (const k of event.uniqueLocalIds) {
    itemsDeleted.push(k);
  }
}

async function handleChange(
  event: Word.ParagraphChangedEventArgs | Word.ParagraphAddedEventArgs
) {
  for (const k of event.uniqueLocalIds) {
    itemsChanged.push(k);
  }
}
Word.run(async (context) => {
  context.document.onParagraphAdded.add(handleChange);
  context.document.onParagraphChanged.add(handleChange);
  context.document.onParagraphDeleted.add(handleDeleted);

  const existingParagraphs = context.document.body.paragraphs;
  existingParagraphs.load("items, uniqueLocalId");
  await context.sync();
  const exisitingIds = existingParagraphs.items.map((x) => x.uniqueLocalId);
  for (const k of existingIds) {
    itemsChanged.push(k);
  }
});
async function getCurrentParagraphIds() {
  const ids = await Word.run(async (context) => {
    const existingParagraphs = context.document.body.paragraphs;
    existingParagraphs.load("items, uniqueLocalId");
    await context.sync();
    const exisitingIds = existingParagraphs.items.map((x) => x.uniqueLocalId);

    return exisitingIds;
  });
  return ids;
}
async function checkForMissedEvents() {
  const ids = await getCurrentParagraphIds();
  const shouldExistIds: Set<string> = new Set(itemsChanged);
  for (const k of itemsDeleted) {
    // Remove all deleted items
    if (shouldExistIds.has(k)) {
        shouldExistIds.delete(k);
    }
  }
  const shouldNotExistIds: Set<string> = new Set(itemsDeleted);

  for (const k of ids) {
      if (!shouldExistIds.has(k)) {
          console.log("ERROR: Update did not exist for: ", k);
      }
      if (shouldNotExistIds.has(k)) {
          console.log("ERROR: Update was told to delete key, but it should exist: ", k);
      }
  }
}

Connect the checkForMissedEvents function up to a button in a Word Add In. Open a document and then add some text to use for editing. Edit the text by copy pasting paragraphs / deleting text etc. Copy a paragraph and paste multiple times very quickly in succession. Triggering the checkForMissedEvents should see if any updates were missed by the events by printing to the console.

Your Environment

Expected behavior

No events for onParagraphAdded, onParagraphDeleted or onParagraphChanged should ever be missed. They can be delayed, but they should fire eventually.

Current behavior

Some events are missed when the document is edited quickly. Especially with copy and paste.

Steps to reproduce

  1. Create a taskpane Word Add-In from a template (any Framework e.g. React)
  2. Copy the above code to execute when the taskpane is mounted. Create a button to fire the checkForMissedEvents function when clicked.
  3. Launch the taskpane in Word on the web and perform a lot of edits on the document. Create multiple paragraphs and mutate the text within. Copy and paste the paragraph several times in quick succession. Press the earlier created button to check to see if any events are missing.

Link to live example(s)

Script Lab did not work for me.

Provide additional details

n/a

Context

The Word Add In I am trying to create needs access to the text of the entire document to process statistics. In order to not slow down the user's machine by running the update on the entire text every time a change is made, we re-designed the system to work by only updating the stats based on changed to paragraphs which reduces the impact on the user. This works for light editing of the text, but only if the events correctly fire on the Word side. No events can be missed, or the statistics will be incorrect.

shanshanzheng-dev commented 2 months ago

Hi @JamieMair Thanks for reporting this issue. we'll take a look and report back if we have a suggestion for you.

shanshanzheng-dev commented 2 months ago

Hi @JamieMair we do some investigations, methods like context.document.onParagraphAdded.add, their parameters should be an async method, otherwise it will hit error. you can modify this, if it is not useful, you can provide us existingIds. Thanks image

JamieMair commented 2 months ago

Changing the event handlers to async should fix that error, but the problem still persists if that fix is made. I have changed the original example to fix this.

shanshanzheng-dev commented 2 months ago

Hi @JamieMair we'll take a look and report back if we have a suggestion for you.

xiruatms commented 1 month ago

@JamieMair , thanks for reporting this.

It seems there's something wrong with the codes you pasted:

  1. itemChanged was used in handleDeleted.
  2. ItemDeleted was used in handleChange

Will you please confirm whether that's expected?

JamieMair commented 1 month ago

@xiruatms Thanks for picking up on this. Yes it is an error in the code pasted, but the underlying error still persists.