Aplikacje Blockly często generują JavaScript jako język wyjściowy, zwykle do uruchamiania na stronie internetowej (możliwe, że tej samej lub w osadzonej przeglądarce WebView). Podobnie jak w przypadku innych generatorów, pierwszym krokiem jest uwzględnienie generatora JavaScript.
import {javascriptGenerator} from 'blockly/javascript';
Aby wygenerować kod JavaScript z obszaru roboczego, wywołaj:
javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);
Wynikowy kod można wykonać bezpośrednio na docelowej stronie internetowej:
try {
eval(code);
} catch (e) {
alert(e);
}
Powyższy fragment kodu generuje kod i go wykonuje. Istnieje jednak kilka ulepszeń. Jednym z ulepszeń jest to, że funkcja eval jest opakowana w try
/catch
, dzięki czemu widoczne są wszystkie błędy czasu działania, a nie tylko ciche błędy. Kolejną zmianą jest dodanie znaku code
do listy słów zastrzeżonych. Dzięki temu, jeśli kod użytkownika zawiera zmienną o tej nazwie, zostanie ona automatycznie zmieniona, zamiast powodować konflikt. W ten sposób należy zarezerwować wszystkie zmienne lokalne.
Bloki wyróżnienia
Podświetlanie aktualnie wykonywanego bloku podczas działania kodu pomaga użytkownikom zrozumieć zachowanie programu. Podświetlanie można przeprowadzić na poziomie poszczególnych instrukcji, ustawiając wartość STATEMENT_PREFIX
przed wygenerowaniem kodu JavaScript:
javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');
Zdefiniuj highlightBlock
, aby oznaczyć blok w obszarze roboczym.
function highlightBlock(id) {
workspace.highlightBlock(id);
}
Spowoduje to dodanie instrukcji highlightBlock('123');
przed każdą instrukcją, gdzie 123
to numer seryjny bloku, który ma zostać wyróżniony.
Infinite Loops
Chociaż wynikowy kod jest zawsze poprawny pod względem składniowym, może zawierać nieskończone pętle. Rozwiązanie problemu zatrzymania wykracza poza zakres Blockly (!), więc najlepszym sposobem postępowania w takich przypadkach jest utrzymywanie licznika i zmniejszanie go za każdym razem, gdy wykonywana jest iteracja.
Aby to zrobić, ustaw wartość javascriptGenerator.INFINITE_LOOP_TRAP
na fragment kodu, który będzie wstawiany do każdej pętli i każdej funkcji. Oto przykład:
window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);
Przykład
Oto demonstracja na żywo generowania i wykonywania kodu JavaScript.
JS-Interpreter
Jeśli zależy Ci na prawidłowym wykonywaniu bloków użytkownika, skorzystaj z projektu JS-Interpreter. Jest on niezależny od Blockly, ale został napisany specjalnie z myślą o nim.
- Uruchamiaj kod z dowolną szybkością.
- Wstrzymywanie/wznawianie/krokowe wykonywanie.
- Wyróżniaj bloki podczas ich wykonywania.
- Całkowicie odizolowany od JavaScriptu przeglądarki.
Uruchamianie interpretera
Najpierw pobierz JS-Interpreter z GitHuba:
Następnie dodaj go do strony:
<script src="acorn_interpreter.js"></script>
Najprostsza metoda wywołania to wygenerowanie kodu JavaScript, utworzenie interpretera i uruchomienie kodu:
var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
Step the Interpreter
Aby wykonać kod wolniej lub w bardziej kontrolowany sposób, zastąp wywołanie funkcji run
pętlą, która wykonuje kroki (w tym przypadku jeden krok co 10 ms):
function nextStep() {
if (myInterpreter.step()) {
setTimeout(nextStep, 10);
}
}
nextStep();
Pamiętaj, że każdy krok nie jest wierszem ani blokiem, ale jednostką semantyczną w JavaScript, która może być bardzo szczegółowa.
Dodaj interfejs API
JS-Interpreter to piaskownica całkowicie odizolowana od przeglądarki. Wszystkie bloki, które wykonują działania w świecie zewnętrznym, wymagają dodania do interpretera interfejsu API. Pełny opis znajdziesz w dokumentacji JS-Interpreter. Na początek podajemy interfejs API potrzebny do obsługi bloków alertów i promptów:
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));
}
Następnie zmodyfikuj inicjowanie interpretera, aby przekazywać funkcję initApi:
var myInterpreter = new Interpreter(code, initApi);
Bloki alertu i promptu to jedyne 2 bloki w domyślnym zestawie bloków, które wymagają niestandardowego interfejsu API dla interpretera.
Łączę z dostawcą highlightBlock()
Gdy kod jest wykonywany w JS-Interpreter, funkcja highlightBlock()
powinna być wykonywana natychmiast, poza piaskownicą, w miarę jak użytkownik przechodzi przez program. Aby to zrobić, utwórz funkcję opakowującą highlightBlock()
, która przechwyci argument funkcji, i zarejestruj ją jako funkcję natywną.
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));
}
Bardziej zaawansowane aplikacje mogą wielokrotnie wykonywać kroki bez wstrzymywania, dopóki nie zostanie osiągnięte polecenie podświetlenia, a następnie wstrzymać działanie. Ta strategia symuluje wykonywanie kodu wiersz po wierszu. W poniższym przykładzie zastosowano to podejście.
Przykład JS-Interpreter
Oto interaktywna demonstracja interpretacji kodu JavaScript krok po kroku. Ta wersja demonstracyjna zawiera blok oczekiwania, który jest dobrym przykładem do wykorzystania w przypadku innych zachowań asynchronicznych (np. mowy lub dźwięku, danych wejściowych użytkownika).