Skip to content

[word] (Performance) Add a sample highlighting the perf benefits of the correlated objects pattern #959

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

Merged
merged 1 commit into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions playlists-prod/word.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,15 @@
group: Scenarios
api_set:
WordApi: '1.3'
- id: word-scenarios-correlated-objects-pattern
name: Correlated objects pattern
fileName: correlated-objects-pattern.yaml
description: Shows the performance benefits of avoiding `context.sync` calls in a loop.
rawUrl: >-
https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/90-scenarios/correlated-objects-pattern.yaml
group: Scenarios
api_set:
WordApi: '1.4'
- id: word-insert-and-change-content-controls
name: Content control basics
fileName: insert-and-change-content-controls.yaml
Expand Down
9 changes: 9 additions & 0 deletions playlists/word.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,15 @@
group: Scenarios
api_set:
WordApi: '1.3'
- id: word-scenarios-correlated-objects-pattern
name: Correlated objects pattern
fileName: correlated-objects-pattern.yaml
description: Shows the performance benefits of avoiding `context.sync` calls in a loop.
rawUrl: >-
https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/90-scenarios/correlated-objects-pattern.yaml
group: Scenarios
api_set:
WordApi: '1.4'
- id: word-insert-and-change-content-controls
name: Content control basics
fileName: insert-and-change-content-controls.yaml
Expand Down
171 changes: 171 additions & 0 deletions samples/word/90-scenarios/correlated-objects-pattern.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
order: 3
id: word-scenarios-correlated-objects-pattern
name: Correlated objects pattern
description: Shows the performance benefits of avoiding `context.sync` calls in a loop.
author: OfficeDev
host: WORD
api_set:
WordApi: '1.4'
script:
content: |
$("#replace-placeholders").on("click", () => tryCatch(replacePlaceholders)); $("#replace-placeholders-slow").on("click", () => tryCatch(replacePlaceholdersSlow)); $("#setup").on("click", () => tryCatch(setup)); $("#add-lots-of-text").on("click", () => tryCatch(addLotsOfText));
const jobMapping = [
{ job: "{Coordinator}", person: "Sally" },
{ job: "{Deputy}", person: "Bob" },
{ job: "{Manager}", person: "Kim" }
];
async function replacePlaceholders() {
Word.run(async (context) => {
const startTime = Date.now();
let count = 0;

// Find the locations of all the placeholder strings.
const allSearchResults = [];
for (let i = 0; i < jobMapping.length; i++) {
let options = Word.SearchOptions.newObject(context);
options.matchWildcards = false;
let searchResults = context.document.body.search(jobMapping[i].job, options);
searchResults.load('items');
let correlatedSearchResult = {
rangesMatchingJob: searchResults,
personAssignedToJob: jobMapping[i].person
}
allSearchResults.push(correlatedSearchResult);
}

// Sync to load those locations in the add-in.
await context.sync()

// Replace the placeholder text at the known locations.
for (let i = 0; i < allSearchResults.length; i++) {
let correlatedObject = allSearchResults[i];

for (let j = 0; j < correlatedObject.rangesMatchingJob.items.length; j++) {
let targetRange = correlatedObject.rangesMatchingJob.items[j];
let name = correlatedObject.personAssignedToJob;
targetRange.insertText(name, Word.InsertLocation.replace);
count++;
}
}

await context.sync();
console.log(`Replacing ${count} placeholders with the correlated objects pattern took ${Date.now() - startTime} milliseconds.`);
console.log()
});
}
async function replacePlaceholdersSlow() {
Word.run(async (context) => {
const startTime = Date.now();
let count = 0;

// The context.sync calls in the loops will degrade performance.
for (let i = 0; i < jobMapping.length; i++) {
let options = Word.SearchOptions.newObject(context);
options.matchWildcards = false;
let searchResults = context.document.body.search(jobMapping[i].job, options);
searchResults.load('items');

await context.sync();

for (let j = 0; j < searchResults.items.length; j++) {
searchResults.items[j].insertText(jobMapping[i].person, Word.InsertLocation.replace);
count++;
await context.sync();
}
}
console.log(`Replacing ${count} placeholders with in-loop sync statements took ${Date.now() - startTime} milliseconds.`);
});
}
async function setup(timesToAddText: number = 1) {
await Word.run(async (context) => {
console.log("Setup beginning...");
const body: Word.Body = context.document.body;
body.clear();
while (timesToAddText > 0) {
body.insertParagraph(
"This defines the roles of {Coordinator}, {Deputy}, {Manager}.",
Word.InsertLocation.end
);
body.insertParagraph(
"{Coordinator}: Oversees daily operations and ensures projects run smoothly by coordinating between different teams and resources.",
Word.InsertLocation.end
);
body.insertParagraph(
"{Deputy}: Assists and supports senior management, often stepping in to make decisions or manage tasks in {Manager}'s absence.",
Word.InsertLocation.end
);
body.insertParagraph(
"{Manager}: Leads the team, setting goals, planning strategies, and making decisions to achieve organizational objectives.",
Word.InsertLocation.end
);
timesToAddText--;
}
await context.sync();
console.log("Setup complete.");
});
}
async function addLotsOfText() {
// Add the setup text 100 times.
setup(100);
}
// Default helper for invoking an action and handling errors.
async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through your add-in's UI.
console.error(error);
}
}
language: typescript
template:
content: |-
<section class="ms-Fabric ms-font-m">
<p>This sample demonstrates the performance optimization gained from the correlated objects pattern. For more information, see <a href="https://learn.microsoft.com/en-us/office/dev/add-ins/concepts/correlated-objects-pattern">Avoid using the context.sync method in loops</a>.</p>
</section>

<section class="ms-Fabric samples ms-font-m">
<h3>Setup</h3>
<p>Use <b>Setup</b> to create a basic text block with some placeholders. Use <b>More text</b> to create a longer text block to better see performance distinctions.</p>
<button id="setup" class="ms-Button">
<span class="ms-Button-label">Setup</span>
</button><button id="add-lots-of-text" class="ms-Button">
<span class="ms-Button-label">More text</span>
</button>
</section>

<section class="ms-Fabric samples ms-font-m">
<h3>Replace placeholder text</h3>
<button id="replace-placeholders" class="ms-Button">
<span class="ms-Button-label">Replace all</span>
</button>
<button id="replace-placeholders-slow" class="ms-Button">
<span class="ms-Button-label">Replace all (slow)</span>
</button>
</section>
language: html
style:
content: |-
section.samples {
margin-top: 20px;
}

section.samples .ms-Button, section.setup .ms-Button {
display: block;
margin-bottom: 5px;
margin-left: 20px;
min-width: 80px;
}
language: css
libraries: |
https://appsforoffice.microsoft.com/lib/1/hosted/office.js
@types/office-js

[email protected]/dist/css/fabric.min.css
[email protected]/dist/css/fabric.components.min.css

[email protected]/client/core.min.js
@types/core-js

[email protected]
@types/[email protected]
1 change: 1 addition & 0 deletions view-prod/word.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"word-document-compare-documents": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/50-document/compare-documents.yaml",
"word-scenarios-doc-assembly": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/90-scenarios/doc-assembly.yaml",
"word-scenarios-multiple-property-set": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/90-scenarios/multiple-property-set.yaml",
"word-scenarios-correlated-objects-pattern": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/90-scenarios/correlated-objects-pattern.yaml",
"word-insert-and-change-content-controls": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/99-preview-apis/insert-and-change-content-controls.yaml",
"word-manage-comments": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/prod/samples/word/99-preview-apis/manage-comments.yaml"
}
1 change: 1 addition & 0 deletions view/word.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"word-document-compare-documents": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/50-document/compare-documents.yaml",
"word-scenarios-doc-assembly": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/90-scenarios/doc-assembly.yaml",
"word-scenarios-multiple-property-set": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/90-scenarios/multiple-property-set.yaml",
"word-scenarios-correlated-objects-pattern": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/90-scenarios/correlated-objects-pattern.yaml",
"word-insert-and-change-content-controls": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/99-preview-apis/insert-and-change-content-controls.yaml",
"word-manage-comments": "https://raw.githubusercontent.com/OfficeDev/office-js-snippets/main/samples/word/99-preview-apis/manage-comments.yaml"
}