Tạo và chạy JavaScript

Các ứng dụng Blockly thường tạo JavaScript làm ngôn ngữ đầu ra, thường là để chạy trong một trang web (có thể là trang web tương tự hoặc WebView được nhúng). Giống như mọi trình tạo, bước đầu tiên là thêm trình tạo JavaScript.

import {javascriptGenerator} from 'blockly/javascript';

Để tạo JavaScript từ không gian làm việc, hãy gọi:

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

Bạn có thể thực thi mã kết quả ngay trong trang web đích:

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

Về cơ bản, đoạn mã trên chỉ tạo mã và đánh giá mã đó. Tuy nhiên, có một số điểm cần tinh chỉnh. Một điểm tinh chỉnh là eval được bao bọc trong try/catch để mọi lỗi thời gian chạy đều hiển thị, thay vì lỗi âm thầm. Một điểm tinh chỉnh khác là code được thêm vào danh sách các từ dành riêng để nếu mã của người dùng chứa một biến có tên đó, thì biến đó sẽ tự động được đổi tên thay vì xung đột. Mọi biến cục bộ đều phải được đặt trước theo cách này.

Khối nổi bật

Việc làm nổi bật khối đang thực thi khi mã chạy giúp người dùng hiểu được hành vi của chương trình. Bạn có thể thực hiện việc làm nổi bật ở cấp độ từng câu lệnh bằng cách đặt STATEMENT_PREFIX trước khi tạo mã JavaScript:

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

Xác định highlightBlock để đánh dấu khối trên không gian làm việc.

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

Điều này dẫn đến việc câu lệnh highlightBlock('123'); được thêm vào trước mỗi câu lệnh, trong đó 123 là số sê-ri của khối cần được làm nổi bật.

Vòng lặp vô hạn

Mặc dù mã kết quả luôn đảm bảo có cú pháp chính xác, nhưng mã này có thể chứa các vòng lặp vô hạn. Vì việc giải quyết Vấn đề dừng nằm ngoài phạm vi của Blockly (!) nên cách tốt nhất để xử lý những trường hợp này là duy trì một bộ đếm và giảm bộ đếm mỗi khi một lần lặp được thực hiện. Để thực hiện việc này, bạn chỉ cần đặt javascriptGenerator.INFINITE_LOOP_TRAP thành một đoạn mã sẽ được chèn vào mọi vòng lặp và mọi hàm. Sau đây là một ví dụ:

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

Ví dụ:

Sau đây là bản minh hoạ trực tiếp về cách tạo và thực thi JavaScript.

JS-Interpreter

Nếu bạn muốn chạy các khối của người dùng một cách thích hợp, thì dự án JS-Interpreter là lựa chọn phù hợp. Dự án này tách biệt với Blockly, nhưng được viết riêng cho Blockly.

  • Thực thi mã ở mọi tốc độ.
  • Tạm dừng/tiếp tục/thực thi từng bước.
  • Đánh dấu các khối khi chúng thực thi.
  • Hoàn toàn tách biệt với JavaScript của trình duyệt.

Chạy trình thông dịch

Trước tiên, hãy tải JS-Interpreter xuống từ GitHub:

Sau đó, hãy thêm mã này vào trang của bạn:

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

Phương thức đơn giản nhất để gọi phương thức này là tạo JavaScript, tạo trình thông dịch và chạy mã:

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

Bước Phiên dịch

Để thực thi mã chậm hơn hoặc theo cách có kiểm soát hơn, hãy thay thế lệnh gọi đến run bằng một vòng lặp theo các bước (trong trường hợp này là một bước sau mỗi 10 mili giây):

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

Xin lưu ý rằng mỗi bước không phải là một dòng hoặc một khối, mà là một đơn vị ngữ nghĩa trong JavaScript, có thể cực kỳ chi tiết.

Thêm một API

JS-Interpreter là một hộp cát hoàn toàn tách biệt với trình duyệt. Mọi khối thực hiện các thao tác với thế giới bên ngoài đều yêu cầu một API được thêm vào trình thông dịch. Để xem nội dung mô tả đầy đủ, hãy xem tài liệu về JS-Interpreter. Nhưng để bắt đầu, đây là API cần thiết để hỗ trợ các khối cảnh báo và lời nhắc:

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

Sau đó, hãy sửa đổi quá trình khởi động trình thông dịch để truyền hàm initApi:

var myInterpreter = new Interpreter(code, initApi);

Khối cảnh báo và khối lời nhắc là 2 khối duy nhất trong bộ khối mặc định yêu cầu một API tuỳ chỉnh cho trình thông dịch.

Đang kết nối với highlightBlock()

Khi chạy trong JS-Interpreter, highlightBlock() sẽ được thực thi ngay lập tức, bên ngoài hộp cát, khi người dùng thực hiện từng bước trong chương trình. Để làm việc này, hãy tạo một hàm bao bọc highlightBlock() để nắm bắt đối số hàm và đăng ký đối số đó dưới dạng một hàm gốc.

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

Các ứng dụng phức tạp hơn có thể muốn thực hiện các bước nhiều lần mà không tạm dừng cho đến khi đạt được lệnh đánh dấu, sau đó tạm dừng. Chiến lược này mô phỏng việc thực thi từng dòng. Ví dụ dưới đây sử dụng phương pháp này.

Ví dụ về JS-Interpreter

Sau đây là một bản minh hoạ trực tiếp về cách diễn giải JavaScript từng bước. Ngoài ra, bản minh hoạ này có một khối chờ, đây là một ví dụ hay để sử dụng cho hành vi không đồng bộ khác (ví dụ: lời nói hoặc âm thanh, dữ liệu đầu vào của người dùng).