In UI process, I want to catch custom Error throw in Webworker with comlink.
The custom Error is extended Error, so comlink serialize/deserialize to Error not to custom Error.
serialize
if (value instanceof Error) {
serialized = {
isError: true,
value: {
message: value.message,
name: value.name,
stack: value.stack,
},
};
}
https://github.com/GoogleChromeLabs/comlink/blob/520e4d64072bc07c3c60dd67dd95910a23602445/src/comlink.ts#L248
deserialize
if (serialized.isError) {
throw Object.assign(
new Error(serialized.value.message),
serialized.value
);
}
https://github.com/GoogleChromeLabs/comlink/blob/520e4d64072bc07c3c60dd67dd95910a23602445/src/comlink.ts#L265
I tried to customize throwTransferHandler to throw custom Error, but It did not work. Because throwMarker is unique Symbol and not exported.
canHandle: (value): value is ThrownValue =>
isObject(value) && throwMarker in value,
https://github.com/GoogleChromeLabs/comlink/blob/520e4d64072bc07c3c60dd67dd95910a23602445/src/comlink.ts#L245
I ended up defining local module which reexports comlink and use serialize-error as custom error serialization/deserialization
import * as Comlink from "comlink";
import ErrorCodec from "serialize-error";
export * from "comlink";
interface ThrownValue {
value: unknown;
}
type SerializedThrownValue =
| { isError: true; value: ErrorCodec.ErrorObject }
| { isError: false; value: unknown };
const throwTransferHandler_:
| Comlink.TransferHandler<unknown, unknown>
| undefined = Comlink.transferHandlers.get("throw");
const throwTransferHandler = throwTransferHandler_ as Comlink.TransferHandler<
ThrownValue,
SerializedThrownValue
>;
const throwTransferHandlerCustom: Comlink.TransferHandler<
ThrownValue,
SerializedThrownValue
> = {
canHandle: throwTransferHandler.canHandle,
serialize: ({ value }) => {
let serialized: SerializedThrownValue;
if (value instanceof Error) {
serialized = {
isError: true,
value: ErrorCodec.serializeError(value),
};
} else {
serialized = { isError: false, value };
}
return [serialized, []];
},
deserialize: (serialized) => {
if (serialized.isError) {
const error = ErrorCodec.deserializeError(serialized.value);
throw error;
}
throw serialized.value;
},
};
Comlink.transferHandlers.set("throw", throwTransferHandlerCustom);
You should make sure that this version of module is imported in all other places instead of direct comlink import
As an alternative In canHandle you can also use getOwnPropertySymbols and toString:
Object.getOwnPropertySymbols({[Symbol("Comlink.thrown")]: 1})
.some(_ => _.toString() === 'Symbol(Comlink.thrown)')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With