Freigeben über


Batchaufrufe von benutzerdefinierten Funktionen für einen Remotedienst

Verwenden Sie Batchverarbeitung, um Aufrufe an einen Remotedienst in einer einzelnen Netzwerkanforderung zu gruppieren. Dies reduziert die Anzahl von Netzwerk-Roundtrips zu Ihrem Remotedienst und hilft Ihnen, die Neuberechnung Ihres Arbeitsblatts schneller abzuschließen.

Hier sehen Sie ein Beispielszenario für die Batchverarbeitung: Wenn 100 Zellen die benutzerdefinierte Funktion aufrufen, senden Sie eine Anforderung, die alle 100 Vorgänge auflistet. Der Dienst gibt 100 Ergebnisse in einer einzelnen Antwort zurück.

Wichtige Punkte

Um die Bündelung für Ihre benutzerdefinierten Funktionen einzurichten, müssen Sie drei wesentliche Codeabschnitte schreiben.

  1. Ein Pushvorgang zum Hinzufügen eines neuen Vorgangs zum Batch von Aufrufen jedes Mal, wenn Excel Ihre benutzerdefinierte Funktion aufruft.
  2. Eine Funktion zum Ausführen der Remoteanforderung , wenn der Batch bereit ist.
  3. Servercode, um auf die Batchanforderung zu reagieren, alle Vorgangsergebnisse zu berechnen und die Werte zurückzugeben.

Wichtig

Beachten Sie, dass benutzerdefinierte Excel-Funktionen auf den folgenden Plattformen verfügbar sind.

  • Office im Web
  • Office unter Windows
    • Microsoft 365-Abonnement
    • retail unbefristete Office 2016 und höher
    • volumelizenzierte unbefristete/LTSC-Office 2021 und höher
  • Office für Mac

Benutzerdefinierte Excel-Funktionen werden derzeit in den folgenden Artikeln nicht unterstützt:

  • Office auf dem iPad
  • Volumenlizenzierte unbefristete Versionen von Office 2021 oder früher unter Windows

Hinweis

Das einheitliche Manifest für Microsoft 365 unterstützt derzeit keine Projekte für benutzerdefinierte Funktionen. Sie müssen das Add-In-Manifest nur für projekte mit benutzerdefinierten Funktionen verwenden. Weitere Informationen finden Sie unter Office-Add-Ins-Manifest.

Erstellen Sie das Batchmuster in diesem Artikel

In den folgenden Abschnitten erfahren Sie, wie Sie den Code beispielweise erstellen. Es wird empfohlen, mithilfe des Yeoman-Generators für Office-Add-Ins ein brandneues Projekt für benutzerdefinierte Funktionen zu erstellen. Informationen zum Erstellen eines neuen Projekts finden Sie unter Erste Schritte bei der Entwicklung benutzerdefinierter Excel-Funktionen. Sie können TypeScript oder JavaScript verwenden.

Tipp

Um das fertige Beispiel anzuzeigen, erstellen Sie ein neues Projekt für benutzerdefinierte Funktionen mit dem Yeoman-Generator für Office-Add-Ins. Kopieren Sie die Codebeispiele, fügen Sie sie in Ihr Projekt ein, führen Sie den Code aus, und testen Sie ihn.

Alternativ können Sie das vollständige Beispielprojekt unter Batchverarbeitungsmuster für benutzerdefinierte Funktionen herunterladen oder anzeigen. Wenn Sie den gesamten Code anzeigen möchten, bevor Sie fortfahren, sehen Sie sich die Skriptdatei an.

Stapeln jedes Aufrufs an Ihre benutzerdefinierte Funktion

Ihre benutzerdefinierten Funktionen funktionieren, indem sie einen Remotedienst aufrufen, um Vorgänge auszuführen und Ergebnisse zurückzugeben. Auf diese Weise können sie jeden angeforderten Vorgang in einem Stapel speichern. Später wird gezeigt, wie man eine _pushOperation-Funktion erstellt, um die Vorgänge zu stapeln. Sehen Sie sich zunächst das folgende Codebeispiel an, um zu erfahren, wie Sie von Ihrer benutzerdefinierten Funktion aus aufrufen _pushOperation .

Im folgenden Code führt die benutzerdefinierte Funktion eine Division durch, verlässt sich aber auf einen Remotedienst, um die eigentliche Kalkulation durchzuführen. Es verwendet _pushOperation um den Vorgang zusammen mit anderen Vorgängen an den Remotedienst zu stapeln. Es benennt den Vorgang div2. Sie können jedes beliebige Benennungsschema für Vorgänge verwenden, solange der Remotedienst ebenfalls das gleiche Schema verwendet (mehr zum Remotedienst später). Außerdem werden die Argumente, die der Remotedienst benötigt, um den Vorgang auszuführen, übernommen.

Hinzufügen der div2 benutzerdefinierten Funktion

Fügen Sie den folgenden Code zu Ihrer functions.js- oder functions.ts-Datei hinzu (je nachdem, ob Sie JavaScript oder TypeScript verwendet haben).

/**
 * Divides two numbers using batching
 * @CustomFunction
 * @param dividend The number being divided
 * @param divisor The number the dividend is divided by
 * @returns The result of dividing the two numbers
 */
function div2(dividend, divisor) {
  return _pushOperation("div2", [dividend, divisor]);
}

Hinzufügen globaler Variablen zum Nachverfolgen von Batchanforderungen

Fügen Sie als Nächstes Ihrerfunctions.js- oder functions.ts-Datei zwei globale Variablen hinzu. _isBatchedRequestScheduled ist später wichtig für die Zeitliche Steuerung von Batchaufrufen an den Remotedienst.

let _batch = [];
let _isBatchedRequestScheduled = false;

Hinzufügen der _pushOperation Funktion

Wenn Excel Ihre benutzerdefinierte Funktion aufruft, müssen Sie den Vorgang per Push in das Batcharray übertragen. Der folgende _pushOperation Funktionscode zeigt, wie sie einen neuen Vorgang aus einer benutzerdefinierten Funktion hinzufügen. Es erstellt einen neuen Batch-Eintrag, erstellt eine neue Zusage zur Auflösung oder Ablehnung des Vorgangs und verschiebt den Eintrag in das Batch-Array.

Dieser Code überprüft auch, ob ein Batch eingeplant ist. In diesem Beispiel wird jeder Batch so eingeplant, dass er alle 100 Millisekunden läuft. Sie können diesen Wert je nach Bedarf anpassen. Höhere Werte führen dazu, dass größere Batches an den Remotedienst gesendet werden und eine längere Wartezeit bis der Benutzer die Ergebnisse sieht. Niedrigere Werte neigen dazu, mehr Batches an den Remotedienst zu senden, aber mit einer schnellen Reaktionszeit für die Benutzer.

Die Funktion erstellt ein invocationEntry-Objekt , das den Zeichenfolgennamen des auszuführenden Vorgangs enthält. Wenn Sie beispielsweise zwei benutzerdefinierte Funktionen multiply und divide benannt hätten, können Sie diese als Vorgangsnamen in Ihren Batch-Einträgen wiederverwenden. args enthält die Argumente, die von Excel an Ihre benutzerdefinierte Funktion übergeben wurden. Und schließlich speichern - oder reject -Methoden eine Zusage, resolve die die Vom Remotedienst zurückgegebenen Informationen enthält.

Fügen Sie der functions.js- oder functions.ts-Datei den folgenden Code hinzu.

// This function encloses your custom functions as individual entries,
// which have some additional properties so you can keep track of whether or not
// a request has been resolved or rejected.
function _pushOperation(op, args) {
  // Create an entry for your custom function.
  console.log("pushOperation");
  const invocationEntry = {
    operation: op, // e.g., sum
    args: args,
    resolve: undefined,
    reject: undefined,
  };

  // Create a unique promise for this invocation,
  // and save its resolve and reject functions into the invocation entry.
  const promise = new Promise((resolve, reject) => {
    invocationEntry.resolve = resolve;
    invocationEntry.reject = reject;
  });

  // Push the invocation entry into the next batch.
  _batch.push(invocationEntry);

  // If a remote request hasn't been scheduled yet,
  // schedule it after a certain timeout, e.g., 100 ms.
  if (!_isBatchedRequestScheduled) {
    console.log("schedule remote request");
    _isBatchedRequestScheduled = true;
    setTimeout(_makeRemoteRequest, 100);
  }

  // Return the promise for this invocation.
  return promise;
}

Eine Remoteanforderung stellen

Der Zweck der _makeRemoteRequest-Funktion besteht darin, den Batch von Vorgängen an den Remotedienst zu übermitteln und dann die Ergebnisse an jeder benutzerdefinierten Funktion zurückzugeben. Zuerst wird eine Kopie des Batch-Arrays erstellt. Dadurch können gleichzeitige benutzerdefinierte Funktionsaufrufe aus Excel sofort mit der Batchverarbeitung in einem neuen Array beginnen. Die Kopie wird dann in ein einfacheres Array umgewandelt, das nicht die Zusageinformationen enthält. Es wäre nicht sinnvoll, die Zusagen an einen Remotedienst weiterzugeben, da sie nicht funktionieren würden. Die _makeRemoteRequest-Funktion wird alle Zusagen entweder ablehnen oder lösen, je nachdem, was der Remotedienst zurückgibt.

Fügen Sie der functions.js- oder functions.ts-Datei den folgenden Code hinzu.

// This is a private helper function, used only within your custom function add-in.
// You wouldn't call _makeRemoteRequest in Excel, for example.
// This function makes a request for remote processing of the whole batch,
// and matches the response batch to the request batch.
function _makeRemoteRequest() {
  // Copy the shared batch and allow the building of a new batch while you are waiting for a response.
  // Note the use of "splice" rather than "slice", which will modify the original _batch array
  // to empty it out.
  try{
  console.log("makeRemoteRequest");
  const batchCopy = _batch.splice(0, _batch.length);
  _isBatchedRequestScheduled = false;

  // Build a simpler request batch that only contains the arguments for each invocation.
  const requestBatch = batchCopy.map((item) => {
    return { operation: item.operation, args: item.args };
  });
  console.log("makeRemoteRequest2");
  // Make the remote request.
  _fetchFromRemoteService(requestBatch)
    .then((responseBatch) => {
      console.log("responseBatch in fetchFromRemoteService");
      // Match each value from the response batch to its corresponding invocation entry from the request batch,
      // and resolve the invocation promise with its corresponding response value.
      responseBatch.forEach((response, index) => {
        if (response.error) {
          batchCopy[index].reject(new Error(response.error));
          console.log("rejecting promise");
        } else {
          console.log("fulfilling promise");
          console.log(response);

          batchCopy[index].resolve(response.result);
        }
      });
    });
    console.log("makeRemoteRequest3");
  } catch (error) {
    console.log("error name:" + error.name);
    console.log("error message:" + error.message);
    console.log(error);
  }
}

Anpassen von _makeRemoteRequest für Ihre eigene Lösung

Die _makeRemoteRequest-Funktion ruft _fetchFromRemoteService auf, was, wie Sie später sehen werden, nur ein Modell für den Remotedienst ist. Dies erleichtert das Studieren und Ausführen des Codes in diesem Artikel. Wenn Sie diesen Code jedoch für einen tatsächlichen Remotedienst verwenden möchten, sollten Sie die folgenden Änderungen vornehmen.

  • Entscheiden Sie, wie Sie die Batch-Vorgänge über das Netzwerk serialisieren möchten. Beispielsweise können Sie das Array in einen JSON-Body einfügen.
  • Anstatt _fetchFromRemoteService aufzurufen, müssen Sie den eigentlichen Netzwerkanruf an den Remotedienst durchführen, um den Batch der Vorgänge weiterzuleiten.

Verarbeitung des Batch-Aufrufs auf dem Remotedienst

Der letzte Schritt ist die Abwicklung des Batch-Aufrufs im Remotedienst. Das folgende Codebeispiel zeigt die _fetchFromRemoteService-Funktion. Diese Funktion entpackt jeden Vorgang, führt den angegebenen Vorgang aus und gibt die Ergebnisse zurück. Für Lernzwecke ist die _fetchFromRemoteService-Funktion in diesem Artikel so konzipiert, dass sie in Ihrem Web-Add-In ausgeführt wird und einen Remotedienst simuliert. Sie können diesen Code zu Ihrer functions.js - oder functions.ts-Datei hinzufügen, damit Sie den gesamten Code in diesem Artikel untersuchen und ausführen können, ohne einen tatsächlichen Remotedienst einrichten zu müssen.

Fügen Sie der functions.js- oder functions.ts-Datei den folgenden Code hinzu.

// This function simulates the work of a remote service. Because each service
// differs, modify this function as needed to work with the service you are using.
// This function takes a batch of argument sets and returns a promise that may contain a batch of values.
// NOTE: When implementing this function on a server, also apply an appropriate authentication mechanism
//       to ensure only the correct callers can access it.
async function _fetchFromRemoteService(requestBatch) {
  // Simulate a slow network request to the server.
  console.log("_fetchFromRemoteService");
  await pause(1000);
  console.log("postpause");
  return requestBatch.map((request) => {
    console.log("requestBatch server side");
    const { operation, args } = request;

    try {
      if (operation === "div2") {
        // Divide the first argument by the second argument.
        return {
          result: args[0] / args[1]
        };
      } else if (operation === "mul2") {
        // Multiply the arguments for the given entry.
        const myResult = args[0] * args[1];
        console.log(myResult);
        return {
          result: myResult
        };
      } else {
        return {
          error: `Operation not supported: ${operation}`
        };
      }
    } catch (error) {
      return {
        error: `Operation failed: ${operation}`
      };
    }
  });
}

function pause(ms) {
  console.log("pause");
  return new Promise((resolve) => setTimeout(resolve, ms));
}

Ändern von _fetchFromRemoteService für Ihren Live-Remotedienst

Um die _fetchFromRemoteService Funktion so zu ändern, dass sie in Ihrem Live-Remotedienst ausgeführt wird, nehmen Sie die folgenden Änderungen vor.

  • Abhängig von Ihrer Serverplattform (Node.js oder andere) ordnen Sie den Client Netzwerkanruf dieser Funktion zu.
  • Entfernen Sie diepause-Funktion, die die Netzwerklatenz als Teil des Modells simuliert.
  • Ändern Sie die Funktionsdeklaration, um mit dem übergebenen Parameter zu arbeiten, wenn der Parameter für Netzwerkzwecke geändert wird. Beispielsweise kann es sich anstelle eines Arrays um einen JSON-Body von Batch-Vorgängen handeln, die verarbeitet werden sollen.
  • Ändern Sie die Funktion, um die Vorgänge auszuführen (oder rufen Sie Funktionen auf, die die Vorgänge ausführen).
  • Anwenden eines geeigneten Authentifizierungsmechanismus. Stellen Sie sicher, dass nur die richtigen Anrufer auf die Funktion zugreifen können.
  • Geben Sie den Code in den Remotedienst ein.

Wann sollte eine Batchverarbeitung vermieden werden?

Die Batchverarbeitung fügt eine kleine Verzögerung und zusätzlichen Code hinzu. Vermeiden Sie Batchverarbeitung in den folgenden Szenarien.

Szenario Negative Auswirkungen der Batchverarbeitung Empfehlung
Einzelne oder sehr wenige Aufrufe Zusätzliches Warten auf Timer Dienst direkt aufrufen, wenn die Liste noch leer ist
Sehr große Eingabedaten pro Aufruf Die Anforderung wird möglicherweise zu groß Beschränken Sie die Größe, oder senden Sie diese Anrufe allein.
Einige Aufrufe sind viel langsamer als andere. Ein langsamer Anruf verzögert sich schneller Separates Gruppieren langsamer Typen
Nahezu sofortiges Ergebnis erforderlich (weniger als 50 ms) Timer fügt Verzögerung hinzu Verwenden eines kürzeren Timers oder Überspringen der Batchverarbeitung
Server kombiniert bereits Arbeit Kein Vorteil Überspringen der Batchverarbeitung auf dem Client

Nächste Schritte

Erfahren Sie mehr über die unterschiedlichen Parameter, die Sie in Ihren benutzerdefinierten Funktionen verwenden können. Oder lesen Sie die Grundlagen zum Tätigen eines Webaufrufs über eine benutzerdefinierte Funktion.

Siehe auch