JavaScript generieren und ausführen

Blockly-Anwendungen generieren oft JavaScript als Ausgabesprache, um in der Regel auf einer Webseite (möglicherweise in derselben oder in einem eingebetteten WebView) ausgeführt zu werden. Wie bei jedem Generator muss zuerst der JavaScript-Generator eingebunden werden.

import {javascriptGenerator} from 'blockly/javascript';

Rufen Sie folgenden Befehl auf, um JavaScript-Code 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 lediglich den Code und bewertet ihn. Es gibt jedoch einige Optimierungen. Eine Verfeinerung besteht darin, dass „eval“ in try/catch eingeschlossen ist, sodass Laufzeitfehler sichtbar sind und nicht bei einem Fehler fehlschlagen. Eine weitere Verfeinerung besteht darin, dass code zur Liste der reservierten Wörter hinzugefügt wird. Wenn der Code des Nutzers eine Variable mit diesem Namen enthält, wird diese dann automatisch umbenannt und kommt nicht in Konflikt. Alle lokalen Variablen sollten auf diese Weise reserviert werden.

Hervorhebungsblöcke

Wenn der derzeit ausgeführte Block hervorgehoben wird, während der Code ausgeführt wird, können Nutzer das Verhalten ihres Programms besser verstehen. Die Hervorhebung kann für jede Anweisung einzeln erfolgen, indem STATEMENT_PREFIX vor dem Generieren des JavaScript-Codes festgelegt wird:

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);
}

Dies führt dazu, dass die Anweisung highlightBlock('123'); vor jeder Anweisung hinzugefügt wird, wobei 123 die Seriennummer des zu markierenden Blocks ist.

Endlosschleifen

Obwohl die Syntax des resultierenden Codes garantiert immer korrekt ist, kann er Endlosschleifen enthalten. Da die Lösung des Halting-Problems den Umfang von Blockly (!) übersteigt, besteht der beste Ansatz für die Handhabung dieser Fälle darin, einen Zähler zu pflegen und bei jeder Ausführung einer Iteration zu verringern. Dazu setzen Sie einfach javascriptGenerator.INFINITE_LOOP_TRAP auf ein Code-Snippet, 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 finden Sie eine Live-Demo zum Generieren und Ausführen von JavaScript.

JS-Interpreter

Wenn Sie die Nutzerblöcke ordnungsgemäß ausführen möchten, ist das JS-Interpreter-Projekt die richtige Lösung. Dieses Projekt ist von Blockly getrennt, wurde aber speziell für Blockly entwickelt.

  • Code in jeder Geschwindigkeit ausführen
  • Ausführung pausieren/fortsetzen/schrittweise ausführen
  • Heben Sie Blöcke während ihrer Ausführung hervor.
  • 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 zu Ihrer Seite hinzu:

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

Die einfachste Methode für den Aufruf besteht darin, den JavaScript-Code zu generieren, den Interpreter zu erstellen und den Code auszuführen:

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

Dolmetscher

Damit der Code langsamer oder kontrollierter ausgeführt wird, ersetzen Sie den Aufruf von run durch eine Schleife (in diesem Fall einen Schritt alle 10 ms):

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

Dabei handelt es sich nicht um eine Zeile oder einen Block, sondern um eine semantische Einheit in JavaScript, die unter Umständen sehr fein abgestimmt ist.

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 Dokumentation zum JS-Interpreter. Aber für den Anfang ist hier die API, die zur Unterstützung der Benachrichtigungs- und Aufforderungsblöcke 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 Initialisierung des Interpreters so, dass die Funktion initApi übergeben wird:

var myInterpreter = new Interpreter(code, initApi);

Die Blöcke für Benachrichtigungen und Aufforderungen sind die einzigen beiden Blöcke im Standardsatz von Blöcken, für die eine benutzerdefinierte API für den Interpreter erforderlich ist.

Verknüpfung mit highlightBlock() wird hergestellt

Bei Ausführung im JS-Interpreter 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 es 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));
}

Komplexere Anwendungen möchten unter Umständen Schritte ohne Pause wiederholt ausführen, bis ein Hervorhebungsbefehl erreicht wird, und dann die Ausführung pausieren. Diese Strategie simuliert die Ausführung zeilenweise. Im folgenden Beispiel wird dieser Ansatz verwendet.

JS-Interpreter-Beispiel

Hier findest du eine Live-Demo zur Interpretation von JavaScript. Diese Demo enthält einen Warteblock, der ein gutes Beispiel für andere asynchrone Verhaltensweisen (z.B. Sprache oder Audio, Nutzereingabe) ist.