Tạo và chạy JavaScript

Các ứng dụng chặn thường tạo JavaScript làm ngôn ngữ đầu ra, thường chạy trong một trang web (có thể giống nhau 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);

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à xoá mã đó. Tuy nhiên, có một vài điều chỉnh. Một nhãn tinh lọc là eval được gói trong một try/catch để mọi lỗi thời gian chạy đều hiển thị, thay vì thất bại một cách lặng lẽ. Một 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ủa tên đó, thì mã đó sẽ tự động được đổi tên thay vì bị xung đột. Bạn nên đặt trước mọi biến cục bộ theo cách này.

Đánh dấu khối

Việc làm nổi bật khối hiện đang thực thi khi mã chạy sẽ giúp người dùng hiểu được hành vi của chương trình. Bạn có thể đánh dấu ở 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');

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

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

Kết quả là 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 sẽ được làm nổi bật.

Vòng lặp vô hạn

Mặc dù mã kết quả được đảm bảo luôn có cú pháp chính xác, nhưng mã này có thể chứa vòng lặp vô hạn. Vì việc giải quyết vấn đề về Tạm dừng nằm ngoài phạm vi của Blockly (!) nên phương pháp tốt nhất để xử lý những trường hợp này là duy trì bộ đếm và giảm tần suất lặp lại mỗi khi lặp lại. Để 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. Dưới đây là ví dụ:

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

Ví dụ:

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

Phiên dịch viên JS

Nếu bạn nghiêm túc về việc chạy các quy tắc chặn của người dùng đúng cách, thì dự án JS-Trình diễn giải là cách tốt nhất. 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 thực thi.
  • Hoàn toàn tách biệt với JavaScript của trình duyệt.

Chạy tính năng Phiên dịch

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

Tải tệp ZIP xuống Tải TAR Ball xuống Xem trên GitHub

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

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

Phương pháp đơn giản nhất để gọi mã này là tạo JavaScript, tạo trình diễn giải và chạy mã:

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

Bước giúp phiên dịch viên

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

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

Lưu ý rằng mỗi bước không phải là một dòng hay 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 API

JS-phiên dịch là một hộp cát hoàn toàn tách biệt với trình duyệt. Bất kỳ khối nào thực hiện thao tác với thế giới bên ngoài đều yêu cầu thêm API vào trình thông dịch. Để biết nội dung mô tả đầy đủ, hãy xem tài liệu về Phiên dịch viên JS. Nhưng để bắt đầu, đây là API cần thiết để hỗ trợ tính năng chặn 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 đó, sửa đổi quy trình khởi chạy thông dịch để truyền vào hàm initApi:

var myInterpreter = new Interpreter(code, initApi);

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

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

Khi chạy trong JS-phiên dịch, highlightBlock() phải đượ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 các bước qua chương trình. Để thực hiện việc này, hãy tạo một hàm gói highlightBlock() để ghi lại đối số hàm và đăng ký đối số đó dưới dạng 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));
}

Những ứng dụng phức tạp hơn có thể muốn liên tục thực thi các bước mà không tạm dừng cho đến khi đạt đến lệnh đánh dấu, sau đó tạm dừng. Chiến lược này mô phỏng quá trình thực thi từng dòng. Ví dụ bên dưới sử dụng phương pháp này.

Ví dụ về phiên dịch viên JS

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