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.runusw.) 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.synchinzuzufügen. Da es in diesem Artikel jedoch darum geht, Aufrufe von context.synczu minimieren, ist es tatsächlich verwirrender, ein völlig unnötiges endgültiges context.synchinzuzufügen. Daher lassen wir es in diesem Artikel weg, wenn am Ende von runkeine 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.syncs 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 keine context.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 die text Eigenschaft des paragraph Proxyobjekts aufzufüllen. Stattdessen wird das paragraph -Objekt einem Array hinzugefügt.
  • Die zweite Schleife durchläuft das Array, das von der ersten Schleife erstellt wurde, und protokolliert die der text einzelnen paragraph Elemente. Dies ist möglich, da die context.sync zwischen den beiden Schleifen enthaltene alle text 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.

  1. Ersetzen Sie die Schleife durch zwei Schleifen.
  2. 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.
  3. Rufen Sie context.sync nach der ersten Schleife auf, um die Proxyobjekte mit allen geladenen Eigenschaften aufzufüllen.
  4. 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 keine context.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.syncs 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 verwendetawaitasync/, 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 - und Array.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 - und Array.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.syncin 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.syncnicht 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.syncfolgen. 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.