-
-
Notifications
You must be signed in to change notification settings - Fork 7
Exception Management
When working with background workers in Squadron, exceptions that occur in a worker thread must be serialized to be sent back to the main thread (or whoever called the worker). By default, Squadron handles standard Dart exceptions by wrapping them in a WorkerException. However, you may want to use custom exception types to convey more specific error information.
All exceptions that occur during task execution and need to cross thread boundaries should inherit from WorkerException. This class ensures that the exception can be properly serialized and includes support for stack traces and command identifiers.
To create a custom exception, you need to:
- Inherit from
WorkerException. - Implement the
serialize()method. This method must return aListwhere the first element is a unique type identifier (aString). - Provide a static deserialization method or a top-level function that can reconstruct the exception from the list.
Here is an example of a custom exception for a database service:
import 'package:squadron/squadron.dart';
class DatabaseException extends WorkerException {
DatabaseException(this.errorCode, String message, [StackTrace? stackTrace, int? command])
: super(message, stackTrace, command);
final int errorCode;
// Unique identifier for this exception type
static const String typeId = 'DB_ERROR';
@override
List serialize() => [
typeId,
message,
stackTrace?.toString(),
command,
errorCode,
];
static DatabaseException? deserialize(List props) {
if (props[0] == typeId) {
return DatabaseException(
props[4] as int,
props[1] as String,
SquadronException.loadStackTrace(props[2] as String?),
(props[3] as num?)?.toInt(),
);
}
return null;
}
}For Squadron to know how to reconstruct your custom exception on the receiving side, you must register its deserializer with the ExceptionManager.
Every IWorker (including Worker and WorkerPool) has an exceptionManager. You should register your custom exceptions before starting the worker or pool.
final myWorker = MyWorker();
myWorker.exceptionManager.register(DatabaseException.typeId, DatabaseException.deserialize);When using a WorkerPool, you should register the exception with the pool's exceptionManager. The pool will automatically pass this manager to all workers it creates.
final myPool = MyWorkerPool();
myPool.exceptionManager.register(DatabaseException.typeId, DatabaseException.deserialize);Note: The registration must happen on the "client" side (typically the main thread) so it can deserialize the exceptions it receives from its workers. If your workers also communicate with each other, they may also need to have these exceptions registered.
Inheriting from WorkerException provides several benefits:
- Platform Neutrality: It works seamlessly across both Native (Isolates) and Web (Web Workers) platforms.
- Context Preservation: It carries the original error message and stack trace across the thread boundary, along with the command ID that caused the error.
-
Automatic Wrapping: If a worker method throws an error that is NOT a
SquadronException, Squadron will automatically wrap it in aWorkerExceptionusingerror.toString()as the message.
💖 Support the project! Sponsor d-markey on GitHub.