ข้ามไปยังเนื้อหา

Isolates & compute

async/await แก้เรื่องการรอ (network, disk) — แต่ ไม่ ช่วยเรื่องงาน CPU หนัก การ parse JSON 5MB, resize รูป, หรือรัน crypto hash เป็นการคำนวณแบบ synchronous และรันบน UI isolate ระหว่างที่รัน event loop render frame ไม่ได้ แอปจึงค้าง

คุณ await ออกจากปัญหานี้ไม่ได้ เพราะไม่มีอะไรให้ รอ — CPU กำลังยุ่ง ทางแก้คือย้ายงานไป isolate อื่น เพื่อให้ UI isolate ว่างพอจะ render

สำหรับ “รัน expensive pure function นี้นอก main isolate” แบบทั่วไป Flutter ให้ compute() ส่ง top-level (หรือ static) function กับ argument ของตัวเอง จะสร้าง background isolate ขึ้นมา รัน function ที่นั่น แล้วคืนผลเป็น Future

// top-level function — จำเป็น เพราะต้องรันบน isolate อื่น
List<Item> parseItems(String jsonStr) {
final raw = jsonDecode(jsonStr) as List;
return raw.map((e) => Item.fromJson(e)).toList(); // งานหนัก
}
// บน UI isolate — offload มัน แล้ว await ผลโดยไม่ jank:
final items = await compute(parseItems, hugeJsonString);

UI ยัง render ที่ 60fps ระหว่างที่ parse รันอยู่ที่อื่น compute เป็นเครื่องมือที่ถูก ~90% ของกรณี

สำหรับ worker ที่อยู่ยาวหรือการสื่อสารสองทาง ใช้ Isolate.spawn กับ port — isolate ไม่แชร์ memory จึงคุยกันด้วยการส่ง message ผ่าน SendPort/ReceivePort

flowchart LR
  main["UI isolate
(render ต่อเนื่อง)"] -->|ส่ง message| worker["Worker isolate
(ทำงาน CPU)"]
  worker -->|ส่งผลกลับ| main
  note["ไม่แชร์ memory:
ข้ามได้แค่ message ที่ถูก copy"] --- worker
offload งานหนักไปยัง worker isolate

กฎที่กำหนดทุกอย่าง: isolate ไม่แชร์ memory อะไรที่คุณส่งจะถูก copy ข้าม boundary (มี fast-path ยกเว้นบ้างเช่น TransferableTypedData) ผลที่ตามมา:

  • คุณส่ง closure ที่ capture UI state หรือ object graph ที่คุณ mutate อยู่ไม่ได้
  • ส่งข้อมูลใหญ่มากมีต้นทุนการ copy — บางครั้งการ copy กินส่วนที่ประหยัดไปหมด
  • worker function ต้อง self-contained (top-level/static) รับ plain data เข้าและคืน plain data ออก

นี่คือ message-passing model เดียวกับในบทเรียน Dart async แต่ตอนนี้ใช้เพื่อ parallelism แทน concurrency

ทำไม async/await ถึงไม่แก้ jank จากการ parse JSON ใหญ่?
compute() ทำอะไร?
isolate สื่อสารกันอย่างไร?
ผลของ boundary ที่ไม่แชร์ memory คืออะไร?