สร้างและเรียกใช้ JavaScript

แอปพลิเคชัน Blockly มักจะสร้าง JavaScript เป็นภาษาเอาต์พุต ซึ่งโดยทั่วไปจะทำงานภายในหน้าเว็บ (อาจเป็นหน้าเว็บเดียวกันหรือ WebView ที่ฝังอยู่) ขั้นตอนแรกคือใส่เครื่องมือสร้าง JavaScript เช่นเดียวกับเครื่องมือสร้างอื่นๆ

import {javascriptGenerator} from 'blockly/javascript';

หากต้องการสร้าง JavaScript จากพื้นที่ทํางาน ให้เรียกใช้

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

โค้ดที่ได้จะทํางานในหน้าเว็บปลายทางได้โดยตรง

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

โดยพื้นฐานแล้วข้อมูลโค้ดด้านบนจะสร้างโค้ดและประเมิน อย่างไรก็ตาม ยังมีการปรับแต่งอีก 2-3 อย่าง การปรับปรุงอย่างหนึ่งคือ eval จะถูกรวมอยู่ใน try/catch เพื่อให้เห็นข้อผิดพลาดรันไทม์แทนที่จะทำงานไม่สำเร็จโดยไม่มีการแจ้งเตือน การปรับปรุงอีกอย่างหนึ่งคือมีการเพิ่ม code ลงในรายการคำที่สงวนไว้เพื่อให้ระบบเปลี่ยนชื่อตัวแปรที่มีชื่อดังกล่าวโดยอัตโนมัติแทนที่จะเกิดข้อขัดแย้งหากโค้ดของผู้ใช้มีตัวแปรที่มีชื่อนั้น คุณควรสงวนตัวแปรในเครื่องด้วยวิธีนี้

บล็อกไฮไลต์

การไฮไลต์บล็อกที่กำลังดำเนินการขณะที่โค้ดทำงานช่วยให้ผู้ใช้เข้าใจลักษณะการทํางานของโปรแกรม คุณสามารถไฮไลต์ในระดับคำสั่งได้โดยการตั้งค่า STATEMENT_PREFIX ก่อนสร้างโค้ด JavaScript

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

กำหนด highlightBlock เพื่อทําเครื่องหมายบล็อกในพื้นที่ทํางาน

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

การดำเนินการนี้จะเพิ่มคำสั่ง highlightBlock('123'); ไว้ก่อนคำสั่งทุกรายการ โดยที่ 123 คือหมายเลขซีเรียลของบล็อกที่จะไฮไลต์

วนซ้ำไปเรื่อยๆ

แม้ว่าระบบจะรับประกันว่าโค้ดที่ได้จะถูกต้องตามหลักไวยากรณ์เสมอ แต่โค้ดดังกล่าวอาจมีลูปที่ไม่มีที่สิ้นสุด เนื่องจากการแก้ปัญหาการหยุดทำงานอยู่นอกเหนือขอบเขตของ Blockly (!) วิธีที่ดีที่สุดในการจัดการกับกรณีเหล่านี้คือการคงตัวนับไว้และลดตัวนับทุกครั้งที่มีการดำเนินการซ้ำ โดยเพียงตั้งค่า javascriptGenerator.INFINITE_LOOP_TRAP เป็นข้อมูลโค้ดที่จะแทรกลงในลูปและฟังก์ชันทุกรายการ ตัวอย่างมีดังนี้

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

ตัวอย่าง

ต่อไปนี้คือการสาธิตแบบเรียลไทม์ในการสร้างและเรียกใช้ JavaScript

JS-Interpreter

หากคุณต้องการเรียกใช้บล็อกของผู้ใช้อย่างถูกต้อง โปรเจ็กต์ JS-Interpreter คือสิ่งที่คุณควรใช้ โปรเจ็กต์นี้แยกจาก Blockly แต่เขียนขึ้นเพื่อ Blockly โดยเฉพาะ

  • เรียกใช้โค้ดด้วยความเร็วใดก็ได้
  • หยุดชั่วคราว/เล่นต่อ/ดำเนินการทีละขั้นตอน
  • ไฮไลต์บล็อกขณะที่ดำเนินการ
  • แยกออกจาก JavaScript ของเบราว์เซอร์โดยสมบูรณ์

เรียกใช้อินเทอร์พรีเตอร์

ก่อนอื่น ให้ดาวน์โหลด JS-Interpreter จาก GitHub โดยทำดังนี้

ดาวน์โหลดไฟล์ ZIP ดาวน์โหลด TAR Ball ดูใน GitHub

จากนั้นเพิ่มลงในหน้าเว็บโดยทำดังนี้

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

วิธีที่ง่ายที่สุดในการเรียกใช้คือการสร้าง JavaScript, สร้างโปรแกรมตีความ และเรียกใช้โค้ด ดังนี้

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

ขั้นตอนในการใช้งานโปรแกรมแปล

หากต้องการให้โค้ดทำงานช้าลงหรือควบคุมได้มากขึ้น ให้แทนที่การเรียก run ด้วยลูปที่ดำเนินการทีละขั้น (ในกรณีนี้ 1 ขั้นทุกๆ 10 มิลลิวินาที)

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

โปรดทราบว่าแต่ละขั้นตอนไม่ใช่บรรทัดหรือบล็อก แต่เป็นหน่วยเชิงความหมายใน JavaScript ซึ่งอาจละเอียดมาก

เพิ่ม API

JS-Interpreter คือแซนด์บ็อกซ์ที่แยกจากเบราว์เซอร์โดยสมบูรณ์ บล็อกที่ดำเนินการกับโลกภายนอกจะต้องเพิ่ม API ลงในโปรแกรมแปล ดูคำอธิบายทั้งหมดได้ในเอกสารประกอบเกี่ยวกับ JS-Interpreter แต่ก่อนอื่น มาดู API ที่จําเป็นเพื่อรองรับบล็อกการแจ้งเตือนและพรอมต์กัน

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

จากนั้นแก้ไขการเริ่มต้นอินเทอร์พรีเตอร์เพื่อส่งผ่านฟังก์ชัน initApi ดังนี้

var myInterpreter = new Interpreter(code, initApi);

บล็อกการแจ้งเตือนและพรอมต์เป็นบล็อก 2 บล็อกเดียวในชุดบล็อกเริ่มต้นที่ต้องใช้ API ที่กําหนดเองสําหรับโปรแกรมแปล

กำลังเชื่อมต่อกับ highlightBlock()

เมื่อทำงานใน JS-Interpreter ระบบควรเรียกใช้ highlightBlock() ทันทีนอกแซนด์บ็อกซ์ขณะที่ผู้ใช้ดำเนินการตามโปรแกรม โดยสร้างฟังก์ชัน Wrapper highlightBlock() เพื่อบันทึกอาร์กิวเมนต์ของฟังก์ชัน และลงทะเบียนเป็นฟังก์ชันเนทีฟ

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

แอปพลิเคชันที่มีความซับซ้อนมากขึ้นอาจต้องการเรียกใช้ขั้นตอนซ้ำๆ โดยไม่หยุดชั่วคราวจนกว่าจะถึงคำสั่งไฮไลต์ แล้วจึงหยุดชั่วคราว กลยุทธ์นี้จะจําลองการดําเนินการทีละบรรทัด ตัวอย่างด้านล่างใช้แนวทางนี้

ตัวอย่าง JS-Interpreter

ต่อไปนี้คือการสาธิตแบบเรียลไทม์ของการตีความ JavaScript แบบทีละขั้นตอน และการสาธิตนี้มีบล็อกการรอ ซึ่งเป็นตัวอย่างที่ดีในการใช้กับลักษณะการทำงานแบบไม่พร้อมกันอื่นๆ (เช่น เสียงพูดหรือเสียง อินพุตของผู้ใช้)