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:
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.