Dart Isolates en Concurrency in 2026: compute, Async en Sollicitatievragen
Beheers Dart isolates, de compute-functie en async-patronen in Flutter. Concurrency-modellen, prestatie-optimalisatie en technische sollicitatievragen.

Dart isolates bieden echte parallelle uitvoering in Flutter-applicaties en lossen de single-threaded beperking op die UI-haperingen veroorzaakt tijdens zware berekeningen. In tegenstelling tot threads in andere talen delen isolates geen geheugen en communiceren ze uitsluitend via message passing, waardoor concurrent code veiliger en begrijpelijker wordt.
Dart 3.7 introduceerde lichtgewicht isolates met 50% minder geheugenoverhead. De Isolate.run() API dekt nu de meeste use cases waarvoor eerder handmatige isolate-spawning nodig was, terwijl compute() de standaardkeuze blijft voor Flutter-specifieke workloads.
Het Dart Concurrency Model Begrijpen
Dart voert code uit in een single-threaded event loop binnen elke isolate. Dit betekent dat async/await geen parallelisme biedt - het maakt alleen non-blocking I/O-operaties mogelijk. Wanneer een CPU-intensieve taak op de hoofd-isolate draait, bevriest de UI omdat de event loop geen frame-callbacks kan verwerken.
De oplossing is om zwaar werk naar aparte isolates te verplaatsen. Elke isolate heeft zijn eigen geheugenheap en event loop, draaiend op een aparte thread beheerd door de Dart VM. Communicatie gebeurt via SendPort en ReceivePort, die berichten serialiseren tussen isolates.
import 'dart:isolate';
// Heavy computation that would block the UI if run on main isolate
int computeFibonacci(int n) {
if (n <= 1) return n;
return computeFibonacci(n - 1) + computeFibonacci(n - 2);
}
Future<void> main() async {
// Isolate.run() handles spawning, message passing, and cleanup automatically
final result = await Isolate.run(() => computeFibonacci(40));
print('Fibonacci(40) = $result'); // Computed without blocking main thread
}De Isolate.run() functie geintroduceerd in Dart 2.19 vereenvoudigt het gangbare patroon van het uitvoeren van een enkele berekening in een achtergrond-isolate. Het handelt automatisch port-creatie, berichtserialisatie en isolate-opruiming af.
Gebruik van compute() in Flutter-Applicaties
Flutter biedt de compute() functie specifiek ontworpen voor het verplaatsen van werk van de UI-isolate. In tegenstelling tot de ruwe isolate API's integreert compute() met het Flutter-framework en handelt het de boilerplate van isolate-management af.
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;
// Top-level function required for compute() - cannot be a closure or method
Uint8List _applyGrayscaleFilter(Uint8List imageBytes) {
final image = img.decodeImage(imageBytes)!;
final grayscale = img.grayscale(image);
return Uint8List.fromList(img.encodePng(grayscale));
}
class ImageProcessor {
Future<Uint8List> processImage(Uint8List rawImage) async {
// compute() spawns isolate, runs function, returns result, disposes isolate
return await compute(_applyGrayscaleFilter, rawImage);
}
}De functie die aan compute() wordt doorgegeven moet een top-level functie of een statische methode zijn. Instance methods en closures vangen state op die niet geserialiseerd kan worden over isolate-grenzen.
Het doorgeven van niet-serialiseerbare objecten aan isolates veroorzaakt runtime-fouten. Classes moeten eenvoudige data-objecten zijn - geen BuildContext, geen Stream referenties, geen singletons. Extraheer alleen de primitieve data die nodig is voor de berekening.
Bidirectionele Communicatie met Ports
Complexe scenario's vereisen doorlopende communicatie tussen isolates in plaats van een enkele request-response cyclus. Langlopende achtergrondtaken, streaming dataverwerking en worker pools hebben allemaal bidirectionele message passing nodig.
import 'dart:isolate';
import 'dart:async';
class BackgroundWorker {
late Isolate _isolate;
late SendPort _sendPort;
final _responseController = StreamController<dynamic>.broadcast();
Stream<dynamic> get responses => _responseController.stream;
Future<void> start() async {
final receivePort = ReceivePort();
// Spawn isolate with initial SendPort for bidirectional setup
_isolate = await Isolate.spawn(_workerEntryPoint, receivePort.sendPort);
// First message from worker contains its SendPort
final completer = Completer<SendPort>();
receivePort.listen((message) {
if (message is SendPort) {
completer.complete(message);
} else {
_responseController.add(message); // Forward results to stream
}
});
_sendPort = await completer.future;
}
void sendTask(Map<String, dynamic> task) {
_sendPort.send(task);
}
void dispose() {
_isolate.kill(priority: Isolate.immediate);
_responseController.close();
}
}
// Entry point runs in the background isolate
void _workerEntryPoint(SendPort mainSendPort) {
final workerReceivePort = ReceivePort();
mainSendPort.send(workerReceivePort.sendPort); // Send port back to main
workerReceivePort.listen((message) {
final task = message as Map<String, dynamic>;
final result = _processTask(task); // Heavy computation here
mainSendPort.send(result);
});
}
dynamic _processTask(Map<String, dynamic> task) {
// Simulate CPU-intensive work
return {'taskId': task['id'], 'processed': true};
}Dit patroon vestigt een persistente worker-isolate die meerdere taken accepteert in de loop van de tijd. De hoofd-isolate stuurt taken via SendPort, en de worker antwoordt via zijn eigen port. De state binnen de worker-isolate blijft behouden tussen taken, wat caching en incrementele verwerking mogelijk maakt.
Async/Await vs Isolates: Het Juiste Gereedschap Kiezen
Een veelgestelde sollicitatievraag voor Flutter-ontwikkelaars betreft wanneer async/await te gebruiken versus isolates. Het onderscheid is belangrijk voor applicatieprestaties.
| Scenario | Gebruik async/await | Gebruik isolates | |----------|--------------------|-----------------| | Netwerkverzoeken | Ja | Nee | | Bestand-I/O | Ja | Nee | | JSON parsing (klein) | Ja | Nee | | JSON parsing (>1MB) | Nee | Ja | | Beeldverwerking | Nee | Ja | | Cryptografische operaties | Nee | Ja | | Database queries | Ja | Soms | | Complexe berekeningen | Nee | Ja |
Async/await behandelt I/O-bound operaties efficient omdat de Dart VM het wachten delegeert aan het besturingssysteem. De event loop blijft vrij om UI-events te verwerken. Isolates lossen CPU-bound problemen op waarbij de berekening zelf tijd kost ongeacht I/O.
import 'dart:convert';
import 'package:flutter/foundation.dart';
class JsonService {
// Small payloads: async is sufficient, isolate overhead not justified
Future<Map<String, dynamic>> parseSmallJson(String json) async {
return jsonDecode(json) as Map<String, dynamic>;
}
// Large payloads: isolate prevents UI jank during parsing
Future<List<dynamic>> parseLargeJson(String json) async {
return await compute(_parseJsonList, json);
}
}
List<dynamic> _parseJsonList(String json) {
return jsonDecode(json) as List<dynamic>;
}Klaar om je Flutter gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Foutafhandeling Over Isolate-Grenzen
Fouten die in isolates worden gegooid propageren niet automatisch naar de aanroepende isolate. Zonder correcte afhandeling verdwijnen exceptions stilletjes, wat moeilijk te debuggen problemen veroorzaakt in productie-apps.
import 'dart:isolate';
class IsolateResult<T> {
final T? value;
final Object? error;
final StackTrace? stackTrace;
IsolateResult.success(this.value) : error = null, stackTrace = null;
IsolateResult.failure(this.error, this.stackTrace) : value = null;
bool get isSuccess => error == null;
}
Future<IsolateResult<T>> runSafeIsolate<T>(T Function() computation) async {
try {
final result = await Isolate.run(() {
try {
return IsolateResult<T>.success(computation());
} catch (e, stack) {
return IsolateResult<T>.failure(e, stack);
}
});
return result;
} catch (e, stack) {
// Handle isolate spawn failures
return IsolateResult<T>.failure(e, stack);
}
}
// Usage
Future<void> processData() async {
final result = await runSafeIsolate(() => riskyComputation());
if (result.isSuccess) {
print('Result: ${result.value}');
} else {
print('Error: ${result.error}');
print('Stack: ${result.stackTrace}');
}
}
int riskyComputation() {
throw Exception('Something went wrong');
}Het wrapper-patroon vangt exceptions binnen de isolate op en retourneert ze als data, waardoor fouten naar boven komen in de aanroepende code. Deze aanpak weerspiegelt het Result-patroon uit Rust en sluit aan bij functionele foutafhandelingspraktijken.
Prestatie-Optimalisatie met Isolate Pools
Het spawnen van isolates brengt overhead met zich mee - geheugenallocatie, JIT-compilatie van code in de nieuwe isolate en berichtserialisatie. Voor applicaties die veel onafhankelijke taken verwerken, verbetert het hergebruiken van isolates via een pool de doorvoer.
import 'dart:async';
import 'dart:collection';
import 'dart:isolate';
class IsolatePool {
final int size;
final Queue<_PooledIsolate> _available = Queue();
final List<_PooledIsolate> _all = [];
bool _initialized = false;
IsolatePool({this.size = 4});
Future<void> initialize() async {
if (_initialized) return;
for (int i = 0; i < size; i++) {
final pooled = await _PooledIsolate.spawn();
_all.add(pooled);
_available.add(pooled);
}
_initialized = true;
}
Future<R> execute<R>(R Function() task) async {
if (!_initialized) await initialize();
// Wait for available isolate
while (_available.isEmpty) {
await Future.delayed(const Duration(milliseconds: 10));
}
final isolate = _available.removeFirst();
try {
return await isolate.run(task);
} finally {
_available.add(isolate); // Return to pool
}
}
Future<void> dispose() async {
for (final isolate in _all) {
isolate.dispose();
}
_all.clear();
_available.clear();
}
}
class _PooledIsolate {
final Isolate isolate;
final SendPort sendPort;
final ReceivePort receivePort;
_PooledIsolate._(this.isolate, this.sendPort, this.receivePort);
static Future<_PooledIsolate> spawn() async {
final receivePort = ReceivePort();
final isolate = await Isolate.spawn(_poolWorker, receivePort.sendPort);
final sendPort = await receivePort.first as SendPort;
return _PooledIsolate._(isolate, sendPort, receivePort);
}
Future<R> run<R>(R Function() task) async {
final responsePort = ReceivePort();
sendPort.send([task, responsePort.sendPort]);
return await responsePort.first as R;
}
void dispose() {
isolate.kill();
receivePort.close();
}
}
void _poolWorker(SendPort mainPort) {
final workerPort = ReceivePort();
mainPort.send(workerPort.sendPort);
workerPort.listen((message) {
final task = message[0] as Function;
final replyPort = message[1] as SendPort;
replyPort.send(task());
});
}De pool onderhoudt een vast aantal opgewarmde isolates die klaar staan om werk te accepteren. Taken worden in een wachtrij geplaatst wanneer alle isolates bezet zijn, en voltooide isolates keren direct terug naar de beschikbare pool.
Veelgestelde Sollicitatievragen over Dart Concurrency
Technische sollicitatiegesprekken testen vaak het begrip van Darts concurrency-model. Voorbereiding op deze vragen demonstreert diepgaande kennis voorbij basis Flutter widget-kennis. De Flutter sollicitatievragen module behandelt aanvullende widget- en state management-onderwerpen.
V: Wat gebeurt er als setState() wordt aangeroepen vanuit een isolate?
Het aanroepen van setState() vanuit een achtergrond-isolate faalt omdat BuildContext en widget-state alleen bestaan in het geheugen van de hoofd-isolate. De isolate kan niet direct objecten van de hoofd-isolate benaderen. In plaats daarvan moet het berekende resultaat teruggestuurd worden naar de hoofd-isolate en daar setState() worden aangeroepen:
import 'dart:isolate';
import 'package:flutter/material.dart';
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _result = 0;
bool _computing = false;
Future<void> _computeInBackground() async {
setState(() => _computing = true);
// Computation runs in isolate
final result = await Isolate.run(() => _heavyCalculation(1000000));
// setState called on main isolate after receiving result
setState(() {
_result = result;
_computing = false;
});
}
Widget build(BuildContext context) {
return Column(
children: [
Text('Result: $_result'),
ElevatedButton(
onPressed: _computing ? null : _computeInBackground,
child: _computing
? const CircularProgressIndicator()
: const Text('Compute'),
),
],
);
}
}
int _heavyCalculation(int iterations) {
int sum = 0;
for (int i = 0; i < iterations; i++) {
sum += i;
}
return sum;
}V: Kunnen isolates objecten delen?
Isolates kunnen geen muteerbare objecten delen. Elke isolate heeft zijn eigen heap, en objecten die tussen isolates worden doorgegeven worden geserialiseerd en gekopieerd. Immutable objecten en primitieven kunnen efficient worden overgedragen omdat Dart hun overdracht optimaliseert. Muteerbare state vereist expliciete synchronisatie via message passing.
V: Hoe werkt async/await met isolates?
Async/await binnen een isolate werkt identiek aan de hoofd-isolate - elke isolate heeft zijn eigen event loop. Het awaiten van een Future in een isolate blokkeert niet de event loop van de hoofd-isolate. De Isolate.run() functie zelf retourneert een Future die voltooit wanneer de isolate klaar is, en integreert naadloos met async code op de hoofd-isolate.
Debuggen van Isolate-Gebaseerde Code
Het debuggen van multi-isolate applicaties vereist begrip dat breakpoints en logging zich anders gedragen over isolate-grenzen.
import 'dart:developer' as developer;
import 'dart:isolate';
void debugPrint(String message) {
// developer.log works across isolates and appears in DevTools
developer.log(
message,
name: 'IsolateDebug',
time: DateTime.now(),
);
}
Future<void> debuggableComputation() async {
await Isolate.run(() {
debugPrint('Starting computation in isolate');
// Timeline events visible in DevTools performance tab
developer.Timeline.startSync('HeavyWork');
final result = performHeavyWork();
developer.Timeline.finishSync();
debugPrint('Computation complete: $result');
return result;
});
}
int performHeavyWork() {
int sum = 0;
for (int i = 0; i < 10000000; i++) {
sum += i;
}
return sum;
}De prestatie-weergave van Flutter DevTools toont timeline-events van alle isolates, waardoor parallelle uitvoering gevisualiseerd kan worden en knelpunten geidentificeerd kunnen worden.
De CPU Profiler in DevTools toont welke isolates de meeste verwerkingstijd verbruiken. De flame chart scheidt uitvoering per isolate, waardoor zichtbaar wordt of werk correct verdeeld is.
Conclusie
- Isolates bieden echte paralleliteit in Dart, draaiend op aparte threads met geisoleerde geheugenheaps
- Gebruik
Isolate.run()voor eenvoudige eenmalige berekeningen,compute()voor Flutter-specifieke workloads - Async/await behandelt I/O-bound werk; isolates lossen CPU-bound problemen op die de event loop blokkeren
- Functies die aan isolates worden doorgegeven moeten top-level of statisch zijn - closures en instance methods falen bij serialisatie
- Implementeer foutafhandeling binnen isolates en retourneer fouten als data om te verzekeren dat exceptions correct naar boven komen
- Isolate pools verminderen spawn-overhead voor applicaties die veel onafhankelijke taken verwerken
- De Flutter state management module vult concurrency-kennis aan voor sollicitatievoorbereiding
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Delen
Gerelateerde artikelen

Flutter-prestatieoptimalisatie in 2026: Impeller, rebuilds en best practices
Hoe Flutter-apps in 2026 constant 60 of 120 fps halen met Impeller, gedisciplineerde widget-rebuilds, RepaintBoundary en profilering met DevTools.

Flutter Web vs React in 2026: prestaties, SEO en use cases
Een praktische vergelijking in 2026 van Flutter Web en React: hoe beide renderen, de echte afwegingen in performance en SEO, codevoorbeelden en welke keuze bij welk project past.

Flutter en Firebase in 2026: Authenticatie, Firestore en Interviewvragen
Een complete gids over Firebase-integratie in Flutter: authenticatie, realtime Firestore-operaties, beveiligingsregels en best practices voor moderne mobile apps.