Kenapa Butuh Isolate?
Dart bersifat single-threaded. Event loop menjalankan microtask dan macrotask secara berurutan. Komputasi berat (parsing JSON besar, enkripsi, image processing) akan memblokir UI → aplikasi jank / lag.
Isolate = thread terpisah dengan memory heap sendiri. Tidak berbagi memori → no race condition. Komunikasi lewat message passing (port).
Isolate Dasar: Isolate.spawn
import 'dart:isolate';
void heavyTask(SendPort sendPort) {
// Simulasi komputasi berat
int sum = 0;
for (int i = 0; i < 100_000_000; i++) {
sum += i;
}
sendPort.send(sum); // kirim hasil ke main isolate
}
Future<void> main() async {
final receivePort = ReceivePort();
// Spawn isolate baru, kirim sendPort-nya
await Isolate.spawn(heavyTask, receivePort.sendPort);
// Terima hasil
final result = await receivePort.first;
print('Hasil dari isolate: $result'); // 4999999950000000
receivePort.close();
}
Catatan: Fungsi entry point (
heavyTask) harus top-level ataustatic, bukan closure/lambda.
Pola Request-Response dengan Completer
Agar API bersifat async/await:
Future<int> computeHeavy() {
final receivePort = ReceivePort();
final completer = Completer<int>();
Isolate.spawn((SendPort sendPort) {
int sum = 0;
for (int i = 0; i < 50_000_000; i++) sum += i;
sendPort.send(sum);
}, receivePort.sendPort);
receivePort.listen((message) {
completer.complete(message as int);
receivePort.close();
});
return completer.future;
}
void main() async {
print('Mulai...');
final result = await computeHeavy();
print('Selesai: $result');
}
compute() Helper (Flutter)
Flutter menyediakan shortcut untuk one-off heavy compute:
import 'package:flutter/foundation.dart';
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
void main() async {
// Jalan di isolate terpisah otomatis
final result = await compute(fibonacci, 40);
print('Fibonacci(40) = $result'); // 102334155
}
Keterbatasan compute:
- Hanya 1 argumen (bisa bungkus ke object/record)
- Tidak cocok untuk isolate long-running (streaming, background sync)
Isolate Long-Running: Dua Arah Komunikasi
Butuh ReceivePort di kedua sisi:
// main.dart
void main() async {
final mainReceivePort = ReceivePort();
// Spawn worker
final isolate = await Isolate.spawn<SendPort>(
workerEntry,
mainReceivePort.sendPort,
);
// Kirim port worker ke main
final workerSendPort = await mainReceivePort.first as SendPort;
// Kirim task ke worker
final replyPort = ReceivePort();
workerSendPort.send({'task': 'process', 'data': [1,2,3,4,5], 'replyTo': replyPort.sendPort});
final result = await replyPort.first;
print('Hasil worker: $result');
// Shutdown
workerSendPort.send({'task': 'shutdown'});
isolate.kill(priority: Isolate.immediate);
}
void workerEntry(SendPort mainSendPort) {
final workerReceivePort = ReceivePort();
mainSendPort.send(workerReceivePort.sendPort); // kirim port worker ke main
workerReceivePort.listen((message) {
final msg = message as Map;
if (msg['task'] == 'process') {
final List<int> data = msg['data'];
final sum = data.reduce((a, b) => a + b);
(msg['replyTo'] as SendPort).send(sum);
} else if (msg['task'] == 'shutdown') {
workerReceivePort.close();
Isolate.exit(workerReceivePort.sendPort, 'done');
}
});
}
Best Practices
| Tips | Penjelasan |
|---|---|
Gunakan compute untuk tugas pendek |
Less boilerplate, auto cleanup |
| Hindari kirim object besar | Serialisasi/deserialisasi mahal; gunakan SendPort + ByteData / Uint8List |
| Isolate = biaya startup | ~1-2ms + memory. Jangan spawn di frame render loop |
Gunakan IsolateNameServer untuk named port |
Bisa akses isolate dari mana saja via nama string |
| Error handling | Isolate.spawn return Future<Isolate>; tangani Isolate.exit & addOnExitListener |
Contoh Nyata: Parsing JSON Besar di Background
import 'dart:convert';
import 'package:flutter/foundation.dart';
List<Post> parsePosts(String jsonString) {
final parsed = json.decode(jsonString) as List;
return parsed.map((e) => Post.fromJson(e)).toList();
}
class Post {
final int id;
final String title;
final String body;
Post({required this.id, required this.title, required this.body});
factory Post.fromJson(Map<String, dynamic> j) => Post(
id: j['id'], title: j['title'], body: j['body']
);
}
Future<List<Post>> fetchAndParsePosts() async {
// Simulasi fetch JSON besar (mis. 10k item)
final jsonString = await rootBundle.loadString('assets/posts.json');
// Parse di isolate
return compute(parsePosts, jsonString);
}
Ringkasan
| Konsep | Kapan Dipakai |
|---|---|
Isolate.spawn |
Kontrol penuh, long-running, two-way |
compute() |
One-off heavy function, simpel |
IsolateNameServer |
Akses isolate global by name |
ReceivePort / SendPort |
Message passing (single direction) |
Isolate adalah kunci 60 fps UI saat aplikasi butuh komputasi berat. Gunakan bijak — jangan over-engineer untuk tugas ringan.
Coba sendiri! Buat project Flutter, taruh JSON 50k item di
assets/, bandingkanjson.decodedi main thread vscompute. Ukur denganflutter run --profile+ DevTools Timeline. Share ke sosial media dan tag @ahsai001.