Blockly-Anwendungen generieren häufig JavaScript als Ausgabesprache, in der Regel für die Ausführung auf einer Webseite (möglicherweise derselben oder einer eingebetteten WebView). 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 wird mit dem obigen Snippet nur der Code generiert und ausgewertet. Es gibt jedoch einige Verbesserungen. Eine Verbesserung besteht darin, dass die Auswertung in try
/catch
eingeschlossen ist, sodass alle Laufzeitfehler sichtbar sind und nicht einfach ignoriert werden. Eine weitere Verbesserung besteht darin, dass code
der Liste der reservierten Wörter hinzugefügt wird. Wenn der Code des Nutzers also eine Variable mit diesem Namen enthält, wird sie automatisch umbenannt, anstatt dass es zu einem Konflikt kommt. Alle lokalen Variablen sollten auf diese Weise reserviert werden.
Highlight-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, indem Sie STATEMENT_PREFIX
festlegen, bevor Sie den JavaScript-Code generieren:
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 hervorzuhebenden Blocks ist.
Endlosschleifen
Der resultierende Code ist zwar immer syntaktisch korrekt, kann aber Endlosschleifen enthalten. Da die Lösung des Halting-Problems den Rahmen von Blockly sprengt (!), ist es am besten, einen Zähler zu verwenden und ihn bei jeder Iteration zu dekrementieren.
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 ist 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 Blöcke des Nutzers richtig 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 geschrieben.
- Code mit beliebiger Geschwindigkeit ausführen
- Ausführung pausieren/fortsetzen/schrittweise durchlaufen.
- Blöcke während der Ausführung hervorheben
- Vollständig vom JavaScript des Browsers isoliert.
Interpreter ausführen
Laden Sie zuerst den JS-Interpreter von GitHub herunter:
Fügen Sie es dann auf Ihrer Seite ein:
<script src="acorn_interpreter.js"></script>
Am einfachsten rufen Sie ihn auf, indem Sie den JavaScript-Code generieren, den Interpreter erstellen und den Code ausführen:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Interpreter durchlaufen
Wenn Sie den Code langsamer oder kontrollierter ausführen möchten, ersetzen Sie den Aufruf von run
durch eine Schleife, die in Schritten (in diesem Fall ein Schritt alle 10 ms) ausgeführt wird:
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
Beachten Sie, dass jeder Schritt keine Zeile oder kein Block, sondern eine semantische Einheit in JavaScript ist, die sehr 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 in der realen Welt ausführen, muss dem Interpreter eine API hinzugefügt werden. Eine vollständige Beschreibung finden Sie in der Dokumentation zum JS-Interpreter. Hier ist die API, die für die 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, um die Funktion „initApi“ zu übergeben:
var myInterpreter = new Interpreter(code, initApi);
Die Blöcke „Benachrichtigung“ und „Prompt“ sind die einzigen beiden Blöcke im Standardblockset, für die eine benutzerdefinierte API für den Interpreter erforderlich ist.
Verknüpfung mit highlightBlock()
wird hergestellt
Bei der Ausführung in 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 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 kann es sinnvoll sein, Schritte ohne Unterbrechung wiederholt auszuführen, bis ein Highlight-Befehl erreicht wird, und dann zu pausieren. Bei dieser Strategie wird die zeilenweise Ausführung simuliert. Im folgenden Beispiel wird dieser Ansatz verwendet.
Beispiel für JS-Interpreter
Hier finden Sie eine Live-Demo, in der JavaScript-Code Schritt für Schritt interpretiert wird. Diese Demo enthält einen Warteblock, ein gutes Beispiel für anderes asynchrones Verhalten (z.B. Sprache oder Audio, Nutzereingabe).