Vermeiden der Verwendung der context.sync-Methode in Schleifen
Hinweis
In diesem Artikel wird davon ausgegangen, dass Sie mit mindestens einer der vier anwendungsspezifischen Office JavaScript-APIs – für Excel, Word, OneNote und Visio – arbeiten, die ein Batchsystem für die Interaktion mit dem Office-Dokument verwenden. Insbesondere sollten Sie wissen, was ein Aufruf von context.sync
bewirkt, und Sie sollten wissen, was ein Auflistungsobjekt ist. Wenn Sie sich noch nicht in diesem Stadium befinden, beginnen Sie mit Grundlegendes zur Office JavaScript-API und der Dokumentation, die in diesem Artikel unter "anwendungsspezifisch" verknüpft ist.
Für einige Programmierszenarien in Office-Add-Ins, die eines der anwendungsspezifischen API-Modelle verwenden (für Excel, Word, PowerPoint, OneNote und Visio), muss Ihr Code eine Eigenschaft aus jedem Element eines Auflistungsobjekts lesen, schreiben oder verarbeiten. Beispielsweise ein Excel-Add-In, das die Werte jeder Zelle in einer bestimmten Tabellenspalte abrufen muss, oder ein Word Add-In, das jede instance einer Zeichenfolge im Dokument hervorheben muss. Sie müssen die Member in der items
-Eigenschaft des Auflistungsobjekts durchlaufen. Aus Leistungsgründen müssen Sie jedoch vermeiden, dass in jeder Iteration der Schleife aufgerufen wird context.sync
. Jeder Aufruf von context.sync
ist ein Roundtrip vom Add-In zum Office-Dokument. Wiederholte Roundtrips beeinträchtigen die Leistung, insbesondere wenn das Add-In in Office im Web ausgeführt wird, da die Roundtrips über das Internet erfolgen.
Hinweis
Alle Beispiele in diesem Artikel verwenden for
Schleifen, aber die beschriebenen Methoden gelten für alle Schleifenanweisungen, die ein Array durchlaufen können, einschließlich der folgenden:
for
for of
while
do while
Sie gelten auch für alle Arraymethoden, an die eine Funktion übergeben und auf die Elemente im Array angewendet wird, einschließlich der folgenden:
Array.every
Array.forEach
Array.filter
Array.find
Array.findIndex
Array.map
Array.reduce
Array.reduceRight
Array.some
Schreiben in das Dokument
Im einfachsten Fall schreiben Sie nur in Member eines Auflistungsobjekts, nicht aber deren Eigenschaften. Im folgenden Code werden beispielsweise alle instance von "the" in einem Word Dokument gelb hervorgehoben.
Hinweis
In der Regel empfiehlt es sich, einen Final context.sync
direkt vor dem schließenden Zeichen "}" der Anwendungsfunktion run
(z Excel.run
. B. , Word.run
usw.) zu setzen. Dies liegt daran, dass die run
Funktion einen ausgeblendeten Aufruf von context.sync
als letztes ausführt, wenn befehle in der Warteschlange vorhanden sind, die noch nicht synchronisiert wurden. Die Tatsache, dass dieser Aufruf ausgeblendet ist, kann verwirrend sein, daher wird im Allgemeinen empfohlen, die explizite context.sync
hinzuzufügen. Da es in diesem Artikel jedoch darum geht, Aufrufe von context.sync
zu minimieren, ist es tatsächlich verwirrender, ein völlig unnötiges endgültiges context.sync
hinzuzufügen. Daher lassen wir es in diesem Artikel weg, wenn am Ende von run
keine nicht synchronisierten Befehle vorhanden sind.
await Word.run(async function (context) {
let startTime, endTime;
const docBody = context.document.body;
// search() returns an array of Ranges.
const searchResults = docBody.search('the', { matchWholeWord: true });
searchResults.load('font');
await context.sync();
// Record the system time.
startTime = performance.now();
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.highlightColor = '#FFFF00';
await context.sync(); // SYNCHRONIZE IN EACH ITERATION
}
// await context.sync(); // SYNCHRONIZE AFTER THE LOOP
// Record the system time again then calculate how long the operation took.
endTime = performance.now();
console.log("The operation took: " + (endTime - startTime) + " milliseconds.");
})
Der vorangehende Code hat in einem Dokument mit 200 Instanzen von "die" in Word unter Windows 1 volle Sekunde benötigt. Wenn jedoch die await context.sync();
Zeile innerhalb der Schleife auskommentiert ist und die gleiche Zeile direkt nach der Schleife nicht auskommentiert wurde, dauerte der Vorgang nur eine 1/10stel Sekunde. In Word im Web (mit Edge als Browser) dauerte es 3 volle Sekunden mit der Synchronisierung innerhalb der Schleife und nur 6/10stel Sekunde mit der Synchronisierung nach der Schleife, etwa fünfmal schneller. In einem Dokument mit 2.000 Instanzen von "the" dauerte es (in Word im Web) 80 Sekunden mit der Synchronisierung innerhalb der Schleife und nur 4 Sekunden mit der Synchronisierung nach der Schleife, etwa 20 Mal schneller.
Hinweis
Es lohnt sich zu fragen, ob die Version synchronize-inside-the-loop schneller ausgeführt wird, wenn die Synchronisierungen gleichzeitig ausgeführt werden. Dies könnte durch einfaches Entfernen des await
Schlüsselwort (keyword) von vorn des context.sync()
erfolgen. Dies würde dazu führen, dass die Runtime die Synchronisierung initiiert und dann sofort die nächste Iteration der Schleife startet, ohne auf den Abschluss der Synchronisierung zu warten. Dies ist jedoch keine so gute Lösung wie das Verschieben der context.sync
Schleife vollständig aus diesen Gründen.
- Genau wie die Befehle in einem Synchronisierungsbatchauftrag in die Warteschlange eingereiht werden, werden die Batchaufträge selbst in Office in die Warteschlange eingereiht, aber Office unterstützt nicht mehr als 50 Batchaufträge in der Warteschlange. Alle weiteren Lösen Fehler aus. Wenn also mehr als 50 Iterationen in einer Schleife vorhanden sind, besteht die Möglichkeit, dass die Warteschlangengröße überschritten wird. Je größer die Anzahl der Iterationen ist, desto größer ist die Wahrscheinlichkeit, dass dies geschieht.
- "Gleichzeitig" bedeutet nicht gleichzeitig. Es würde immer noch länger dauern, mehrere Synchronisierungsvorgänge auszuführen, als einen auszuführen.
- Es ist nicht garantiert, dass gleichzeitige Vorgänge in derselben Reihenfolge ausgeführt werden, in der sie gestartet wurden. Im vorherigen Beispiel spielt es keine Rolle, in welcher Reihenfolge das Wort "the" hervorgehoben wird, aber es gibt Szenarien, in denen es wichtig ist, dass die Elemente in der Auflistung in der richtigen Reihenfolge verarbeitet werden.
Lesen von Werten aus dem Dokument mit dem Split-Loop-Muster
Das Vermeiden von context.sync
s innerhalb einer Schleife wird schwieriger, wenn der Code eine Eigenschaft der Sammlungselemente lesen muss, während er jedes verarbeitet. Angenommen, Ihr Code muss alle Inhaltssteuerelemente in einem Word Dokument durchlaufen und den Text des ersten Absatzes protokollieren, der jedem Steuerelement zugeordnet ist. Ihre Programmierkenntnisse können Dazu führen, dass Sie die Steuerelemente durchlaufen, die text
-Eigenschaft jedes (ersten) Absatzes laden, aufrufen context.sync
, um das Proxyabsatzobjekt mit dem Text aus dem Dokument aufzufüllen, und dann protokollieren. Es folgt ein Beispiel.
Word.run(async (context) => {
const contentControls = context.document.contentControls.load('items');
await context.sync();
for (let i = 0; i < contentControls.items.length; i++) {
const paragraph = contentControls.items[i].getRange('Whole').paragraphs.getFirst();
paragraph.load('text');
await context.sync();
console.log(paragraph.text);
}
});
In diesem Szenario sollten Sie ein Muster verwenden, das wir als Split-Schleifenmuster bezeichnen, um zu vermeidencontext.sync
, dass eine -Schleife in einer Schleife ist. Sehen wir uns ein konkretes Beispiel für das Muster an, bevor wir zu einer formalen Beschreibung dieses Musters gelangen. Hier erfahren Sie, wie das Split Loop-Muster auf den vorherigen Codeausschnitt angewendet werden kann. Beachten Sie die folgenden Aspekte in diesem Code.
- Es gibt jetzt zwei Schleifen, und die
context.sync
kommt dazwischen, sodass es keinecontext.sync
der beiden Schleifen gibt. - Die erste Schleife durchläuft die Elemente im Auflistungsobjekt und lädt die
text
Eigenschaft genau wie die ursprüngliche Schleife, aber die erste Schleife kann den Absatztext nicht protokollieren, da sie keine mehr enthält,context.sync
um dietext
Eigenschaft desparagraph
Proxyobjekts aufzufüllen. Stattdessen wird dasparagraph
-Objekt einem Array hinzugefügt. - Die zweite Schleife durchläuft das Array, das von der ersten Schleife erstellt wurde, und protokolliert die der
text
einzelnenparagraph
Elemente. Dies ist möglich, da diecontext.sync
zwischen den beiden Schleifen enthaltene alletext
Eigenschaften aufgefüllt hat.
Word.run(async (context) => {
const contentControls = context.document.contentControls.load("items");
await context.sync();
const firstParagraphsOfCCs = [];
for (let i = 0; i < contentControls.items.length; i++) {
const paragraph = contentControls.items[i].getRange('Whole').paragraphs.getFirst();
paragraph.load('text');
firstParagraphsOfCCs.push(paragraph);
}
await context.sync();
for (let i = 0; i < firstParagraphsOfCCs.length; i++) {
console.log(firstParagraphsOfCCs[i].text);
}
});
Im vorherigen Beispiel wird das folgende Verfahren zum Umwandeln einer Schleife, die ein context.sync
enthält, in das Split-Schleifenmuster vorgeschlagen.
- Ersetzen Sie die Schleife durch zwei Schleifen.
- Create eine erste Schleife, um die Auflistung zu durchlaufen und jedes Element einem Array hinzuzufügen, während gleichzeitig alle Eigenschaften des Elements geladen werden, die ihr Code lesen muss.
- Rufen Sie
context.sync
nach der ersten Schleife auf, um die Proxyobjekte mit allen geladenen Eigenschaften aufzufüllen. - Folgen Sie mit
context.sync
einer zweiten Schleife, um das array zu durchlaufen, das in der ersten Schleife erstellt wurde, und lesen Sie die geladenen Eigenschaften.
Verarbeiten von Objekten im Dokument mit dem Muster korrelierter Objekte
Betrachten wir ein komplexeres Szenario, in dem für die Verarbeitung der Elemente in der Sammlung Daten erforderlich sind, die sich nicht in den Elementen selbst befinden. Das Szenario stellt ein Word-Add-In vor, das mit Dokumenten arbeitet, die aus einer Vorlage mit Textbausteinen erstellt wurden. Der Text enthält eine oder mehrere Instanzen der folgenden Platzhalterzeichenfolgen: "{Coordinator}", "{Deputy}" und "{Manager}". Das Add-In ersetzt jeden Platzhalter durch den Namen einer Person. Die Benutzeroberfläche des Add-Ins ist in diesem Artikel nicht wichtig. Beispielsweise könnte es einen Aufgabenbereich mit drei Textfeldern haben, die jeweils mit einem der Platzhalter beschriftet sind. Der Benutzer gibt einen Namen in jedes Textfeld ein und drückt dann die Schaltfläche Ersetzen . Der Handler für die Schaltfläche erstellt ein Array, das die Namen den Platzhaltern zuordnet, und ersetzt dann jeden Platzhalter durch den zugewiesenen Namen.
Sie müssen kein Add-In mit dieser Benutzeroberfläche erstellen, um mit dem Code zu experimentieren. Sie können das Script Lab-Tool verwenden, um einen Prototyp für den wichtigen Code zu erstellen. Verwenden Sie die folgende Zuweisungsanweisung, um das Zuordnungsarray zu erstellen.
const jobMapping = [
{ job: "{Coordinator}", person: "Sally" },
{ job: "{Deputy}", person: "Bob" },
{ job: "{Manager}", person: "Kim" }
];
Der folgende Code zeigt, wie Sie jeden Platzhalter durch seinen zugewiesenen Namen ersetzen können, wenn Sie innerhalb von Schleifen verwenden context.sync
.
Word.run(async (context) => {
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);
await context.sync();
}
}
});
Im obigen Code gibt es eine äußere und eine innere Schleife. Jede von ihnen enthält eine context.sync
. Basierend auf dem ersten Codeausschnitt in diesem Artikel sehen Sie wahrscheinlich, dass die context.sync
in der inneren Schleife einfach nach der inneren Schleife verschoben werden kann. Dies würde den Code aber trotzdem mit einer context.sync
(tatsächlich zwei) in der äußeren Schleife belassen. Der folgende Code zeigt, wie Sie aus den Schleifen entfernen context.sync
können. Wir besprechen den Code später.
Word.run(async (context) => {
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);
}
await context.sync()
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);
}
}
await context.sync();
});
Beachten Sie, dass der Code das Split-Schleifenmuster verwendet.
- Die äußere Schleife aus dem vorherigen Beispiel wurde in zwei Teile aufgeteilt. (Die zweite Schleife verfügt über eine innere Schleife, die erwartet wird, da der Code eine Reihe von Aufträgen (oder Platzhaltern) durchläuft und innerhalb dieses Satzes die übereinstimmenden Bereiche durchläuft.)
- Es gibt eine
context.sync
nach jeder Hauptschleife, aber keinecontext.sync
innerhalb einer Schleife. - Die zweite Hauptschleife durchläuft ein Array, das in der ersten Schleife erstellt wird.
Das array, das in der ersten Schleife erstellt wurde, enthält jedoch nicht nur ein Office-Objekt wie die erste Schleife im Abschnitt Lesen von Werten aus dem Dokument mit dem Muster für geteilte Schleifen. Dies liegt daran, dass sich einige der informationen, die zum Verarbeiten der Word Range-Objekte erforderlich sind, nicht in den Range-Objekten selbst befinden, sondern stattdessen aus dem jobMapping
Array stammen.
Die Objekte im Array, das in der ersten Schleife erstellt wurde, sind also benutzerdefinierte Objekte, die über zwei Eigenschaften verfügen. Die erste ist ein Array von Word Ranges, die einer bestimmten Position (d. h. einer Platzhalterzeichenfolge) entsprechen, und die zweite ist eine Zeichenfolge, die den Namen der Person bereitstellt, die dem Auftrag zugewiesen ist. Dadurch ist die letzte Schleife einfach zu schreiben und leicht zu lesen, da alle Informationen, die zum Verarbeiten eines bestimmten Bereichs erforderlich sind, in demselben benutzerdefinierten Objekt enthalten sind, das den Bereich enthält. Der Name, der correlatedObject.rangesMatchingJob.items[j] ersetzen soll, ist die andere Eigenschaft desselben Objekts: correlatedObject.personAssignedToJob.
Wir nennen diese Variation des Split-Schleifenmusters das Muster korrelierter Objekte . Die allgemeine Idee ist, dass die erste Schleife ein Array von benutzerdefinierten Objekten erstellt. Jedes Objekt verfügt über eine Eigenschaft, deren Wert eines der Elemente in einem Office-Auflistungsobjekt (oder einem Array solcher Elemente) ist. Das benutzerdefinierte Objekt verfügt über weitere Eigenschaften, von denen jede Informationen bereitstellt, die zum Verarbeiten der Office-Objekte in der letzten Schleife erforderlich sind. Im Abschnitt Weitere Beispiele für diese Muster finden Sie einen Link zu einem Beispiel, bei dem das benutzerdefinierte korrelierende Objekt über mehr als zwei Eigenschaften verfügt.
Ein weiterer Nachteil: Manchmal benötigt es mehr als eine Schleife, um das Array von benutzerdefinierten korrelierenden Objekten zu erstellen. Dies kann der Fall sein, wenn Sie eine Eigenschaft jedes Elements eines Office-Auflistungsobjekts lesen müssen, nur um Informationen zu sammeln, die zum Verarbeiten eines anderen Auflistungsobjekts verwendet werden. (Ihr Code muss beispielsweise die Titel aller Spalten in einer Excel-Tabelle lesen, da Ihr Add-In auf die Zellen einiger Spalten basierend auf dem Titel dieser Spalte ein Zahlenformat anwendet.) Sie können die context.sync
s jedoch immer zwischen den Schleifen und nicht in einer Schleife beibehalten. Ein Beispiel finden Sie im Abschnitt Weitere Beispiele für diese Muster .
Weitere Beispiele für diese Muster
- Ein sehr einfaches Beispiel für Excel, das Schleifen verwendet
Array.forEach
, finden Sie in der akzeptierten Antwort auf diese Stack Overflow-Frage: Ist es möglich, mehr als eine context.load-Warteschlange vor context.sync zu stellen? - Ein einfaches Beispiel für Word, die Schleifen verwendet
Array.forEach
und keine Syntax verwendetawait
async
/, finden Sie in der akzeptierten Antwort auf diese Stack Overflow-Frage: Durchlaufen aller Absätze mit Inhaltssteuerelementen mit der Office JavaScript-API. - Ein Beispiel für Word, das in TypeScript geschrieben ist, finden Sie im Beispiel Word Add-In Angular2 Style Checker, insbesondere in der Datei word.document.service.ts. Es besteht aus einer Mischung aus
for
- undArray.forEach
-Schleifen. - Importieren Sie für ein erweitertes Word Beispiel diesen Gist in das Script Lab-Tool. Informationen zum Kontext bei der Verwendung des Gist finden Sie in der akzeptierten Antwort auf die Stack Overflow-Frage Dokument, das nach dem Ersetzen von Text nicht synchron ist. In diesem Beispiel wird ein benutzerdefinierter korrelierender Objekttyp erstellt, der über drei Eigenschaften verfügt. Es verwendet insgesamt drei Schleifen, um das Array korrelierter Objekte zu erstellen, und zwei weitere Schleifen für die endgültige Verarbeitung. Es gibt eine Mischung aus
for
- undArray.forEach
-Schleifen. - Obwohl es nicht unbedingt ein Beispiel für die Muster für geteilte Schleife oder korrelierte Objekte ist, gibt es ein erweitertes Excel-Beispiel, das zeigt, wie ein Satz von Zellwerten mit nur einem einzigen
context.sync
in andere Währungen konvertiert wird. Um es auszuprobieren, öffnen Sie das Script Lab Tool, suchen Sie nach dem Währungskonverterbeispiel, und navigieren Sie zu diesem.
Wann sollten Sie die Muster in diesem Artikel nicht verwenden?
Excel kann in einem bestimmten Aufruf von context.sync
nicht mehr als 5 MB Daten lesen. Wenn dieser Grenzwert überschritten wird, wird ein Fehler ausgelöst. (Weitere Informationen finden Sie im Abschnitt "Excel-Add-Ins" unter Ressourcenlimits und Leistungsoptimierung für Office-Add-Ins .) Es ist sehr selten, dass dieser Grenzwert erreicht wird, aber wenn die Möglichkeit besteht, dass dies mit Ihrem Add-In geschieht, sollte Ihr Code nicht alle Daten in einer einzelnen Schleife laden und der Schleife mit einem context.sync
folgen. Dennoch sollten Sie vermeiden, context.sync
dass in jeder Iteration einer Schleife über einem Auflistungsobjekt ein -Objekt vorhanden ist. Definieren Sie stattdessen Teilmengen der Elemente in der Auflistung, und durchlaufen Sie jede Teilmenge nacheinander mit einem context.sync
zwischen den Schleifen. Sie könnten dies mit einer äußeren Schleife strukturieren, die die Teilmengen durchläuft und in context.sync
jeder dieser äußeren Iterationen enthält.
Office Add-ins