Freigeben über


Batchaufrufe von benutzerdefinierten Funktionen für einen Remotedienst

Wenn Ihre benutzerdefinierten Funktionen einen Remotedienst aufrufen, können Sie ein Batchmuster verwenden, um die Anzahl der Netzwerkanrufe an den Remotedienst zu reduzieren. Um Netzwerk-Anrufe zu reduzieren, kombinieren Sie alle Anrufe zu einem einzigen Aufruf an den Webdienst. Dies ist ideal, wenn das Arbeitsblatt neu berechnet wird.

Wenn beispielsweise jemand Ihre benutzerdefinierte Funktion in 100 Zellen in einem Arbeitsblatt verwendet und dann das Arbeitsblatt neu berechnet hat, würde Ihre benutzerdefinierte Funktion 100 Mal ausgeführt und 100 Netzwerkanrufe tätigen. Durch die Verwendung eines Batchmusters können die Anrufe kombiniert werden, um alle 100 Kalkulationen in einem einzigen Netzanruf durchzuführen.

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
    • volumenlizenzierte unbefristete 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 2019 oder früher unter Windows

Anzeigen des ausgeführten Beispiels

Um das fertige Beispiel anzuzeigen, folgen Sie diesem Artikel, und fügen Sie die Codebeispiele in Ihr eigenes Projekt ein. Um beispielsweise ein neues projekt für benutzerdefinierte Funktionen für TypeScript zu erstellen, verwenden Sie den Yeoman-Generator für Office-Add-Ins, und fügen Sie dann den gesamten Code aus diesem Artikel dem Projekt hinzu. Führen Sie den Code aus, und probieren Sie ihn aus.

Alternativ können Sie das vollständige Beispielprojekt unter Batchverarbeitungsmuster für benutzerdefinierte Funktionen herunterladen oder anzeigen. Wenn Sie den Code vollständig anzeigen möchten, bevor Sie weiterlesen, werfen Sie einen Blick auf die Skriptdatei.

Erstellen Sie das Batchmuster in diesem Artikel

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.

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.

Stapeln jedes Aufrufs an Ihre benutzerdefinierte Funktion

Ihre benutzerdefinierten Funktionen rufen einen Remotedienst an, um den Vorgang durchzuführen und das benötigte Ergebnis zu berechnen. 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. Werfen Sie zunächst einen Blick auf das folgende Codebeispiel, um zu sehen, wie Sie _pushOperationvon Ihrer benutzerdefinierten Funktion aus aufrufen können.

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 benutzerdefinierten Div2-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, you will need to modify this function appropriately 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.

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