JavaScript generieren und ausführen

Blockly-Anwendungen generieren häufig JavaScript als Ausgabesprache, die in der Regel auf einer Webseite (möglicherweise derselben oder einer eingebetteten WebView) ausgeführt wird. Wie bei jedem Generator müssen Sie zuerst den JavaScript-Generator einbinden.

import {javascriptGenerator} from 'blockly/javascript';

Rufen Sie Folgendes auf, um JavaScript aus dem Arbeitsbereich zu generieren:

javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);

Der resultierende Code kann direkt auf der Zielwebseite ausgeführt werden:

try {
  eval(code);
} catch (e) {
  alert(e);
}

Im Grunde generiert das obige Snippet nur den Code und wertet ihn aus. Es gibt jedoch einige Verbesserungen. Eine Verbesserung besteht darin, dass die eval-Funktion in try/catch eingeschlossen ist, damit alle Laufzeitfehler sichtbar sind, anstatt dass sie stillschweigend fehlschlagen. Außerdem wird code der Liste der reservierten Wörter hinzugefügt. Wenn der Code des Nutzers also eine Variable mit diesem Namen enthält, wird sie automatisch umbenannt, anstatt dass es zu einer Kollision kommt. Alle lokalen Variablen sollten auf diese Weise reserviert werden.

Highlights-Blöcke

Wenn der aktuell ausgeführte Block während der Ausführung des Codes hervorgehoben wird, können Nutzer das Verhalten ihres Programms besser nachvollziehen. Die Hervorhebung kann auf Anweisungsebene erfolgen. Dazu müssen Sie STATEMENT_PREFIX vor dem Generieren des JavaScript-Codes festlegen:

javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');

Definieren Sie highlightBlock, um den Block im Arbeitsbereich zu markieren.

function highlightBlock(id) {
  workspace.highlightBlock(id);
}

Dadurch wird vor jeder Anweisung die Anweisung highlightBlock('123'); eingefügt, wobei 123 die Seriennummer des Blocks ist, der hervorgehoben werden soll.

Endlosschleifen

Der resultierende Code ist zwar immer syntaktisch korrekt, kann aber unendliche Schleifen enthalten. Da die Lösung des Halteproblems nicht in den Zuständigkeitsbereich von Blockly fällt, ist es am besten, in diesen Fällen einen Zähler zu verwenden und ihn bei jeder Iteration zu verringern. Dazu müssen Sie javascriptGenerator.INFINITE_LOOP_TRAP nur auf ein Code-Snippet festlegen, das in jede Schleife und jede Funktion eingefügt wird. Hier ein Beispiel:

window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);

Beispiel

Hier ist eine Live-Demo zum Generieren und Ausführen von JavaScript.

JS-Interpreter

Wenn Sie die Blöcke des Nutzers korrekt ausführen möchten, ist das JS-Interpreter-Projekt die richtige Wahl. Dieses Projekt ist unabhängig von Blockly, wurde aber speziell für Blockly entwickelt.

  • Code mit beliebiger Geschwindigkeit ausführen.
  • Ausführung pausieren/fortsetzen/schrittweise durchlaufen
  • Blöcke werden beim Ausführen hervorgehoben.
  • Vollständig vom JavaScript des Browsers isoliert.

Interpreter ausführen

Laden Sie zuerst den JS-Interpreter von GitHub herunter:

ZIP-Datei herunterladen TAR-Ball herunterladen Auf GitHub ansehen

Fügen Sie sie dann Ihrer Seite hinzu:

<script src="acorn_interpreter.js"></script>

Die einfachste Methode zum Aufrufen besteht darin, das JavaScript zu generieren, den Interpreter zu erstellen und den Code auszuführen:

var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();

Interpreter Schritt für Schritt durchgehen

Wenn Sie den Code langsamer oder kontrollierter ausführen möchten, ersetzen Sie den Aufruf von run durch eine Schleife, die Schritte ausführt (in diesem Fall alle 10 Millisekunden einen Schritt):

function nextStep() {
  if (myInterpreter.step()) {
    setTimeout(nextStep, 10);
  }
}
nextStep();

Jeder Schritt ist keine Zeile oder ein Block, sondern eine semantische Einheit in JavaScript, die extrem detailliert sein kann.

API hinzufügen

Der JS-Interpreter ist eine Sandbox, die vollständig vom Browser isoliert ist. Für alle Blöcke, die Aktionen mit der Außenwelt ausführen, muss dem Interpreter eine API hinzugefügt werden. Eine vollständige Beschreibung finden Sie in der JS-Interpreter-Dokumentation. Hier ist aber zuerst die API, die für die Unterstützung der Blöcke „Benachrichtigung“ und „Aufforderung“ erforderlich ist:

function initApi(interpreter, globalObject) {
  // Add an API function for the alert() block.
  var wrapper = function(text) {
    return alert(arguments.length ? text : '');
  };
  interpreter.setProperty(globalObject, 'alert',
      interpreter.createNativeFunction(wrapper));

  // Add an API function for the prompt() block.
  wrapper = function(text) {
    return prompt(text);
  };
  interpreter.setProperty(globalObject, 'prompt',
      interpreter.createNativeFunction(wrapper));
}

Ändern Sie dann die Interpreterinitialisierung so, dass die Funktion „initApi“ übergeben wird:

var myInterpreter = new Interpreter(code, initApi);

Die Blöcke „alert“ und „prompt“ sind die einzigen beiden Blöcke in der Standardsammlung von Blöcken, für die eine benutzerdefinierte API für den Interpreter erforderlich ist.

Verknüpfung mit highlightBlock() wird hergestellt

Wenn der JS-Interpreter verwendet wird, sollte highlightBlock() sofort außerhalb der Sandbox ausgeführt werden, während der Nutzer das Programm durchläuft. Erstellen Sie dazu eine Wrapper-Funktion highlightBlock(), um das Funktionsargument zu erfassen, und registrieren Sie sie als native Funktion.

function initApi(interpreter, globalObject) {
  // Add an API function for highlighting blocks.
  var wrapper = function(id) {
    return workspace.highlightBlock(id);
  };
  interpreter.setProperty(globalObject, 'highlightBlock',
      interpreter.createNativeFunction(wrapper));
}

Bei komplexeren Anwendungen können Schritte wiederholt ohne Pause ausgeführt werden, bis ein Befehl zum Hervorheben erreicht wird, der dann eine Pause auslöst. Diese Strategie simuliert die zeilenweise Ausführung. Im folgenden Beispiel wird dieser Ansatz verwendet.

Beispiel für einen JS-Interpreter

In dieser Live-Demo wird Schritt für Schritt gezeigt, wie JavaScript interpretiert wird. Diese Demo enthält einen Warteblock, ein gutes Beispiel für anderes asynchrones Verhalten (z.B. Sprache oder Audio, Nutzereingabe).