mirror of
https://github.com/motion-canvas/motion-canvas.git
synced 2026-01-13 07:48:03 -05:00
feat: signal error handling (#89)
- Signal calculations are wrapped in try/catch blocks. - Signals keep track of their owner for better logging.
This commit is contained in:
@@ -12,7 +12,7 @@ export function computed(): MethodDecorator {
|
||||
return (target: any, key) => {
|
||||
addInitializer(target, (instance: any) => {
|
||||
const method = Object.getPrototypeOf(instance)[key];
|
||||
instance[key] = createComputed(method.bind(instance));
|
||||
instance[key] = createComputed(method.bind(instance), instance);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -209,6 +209,7 @@ export abstract class GeneratorScene<T>
|
||||
'Tried to access an asynchronous property before the node was ready. ' +
|
||||
'Make sure to yield the node before accessing the property.',
|
||||
stack: promises[0].stack,
|
||||
inspect: promises[0].owner?.key ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,17 +5,20 @@ import {
|
||||
finishCollecting,
|
||||
startCollecting,
|
||||
} from './createSignal';
|
||||
import {useLogger} from './useProject';
|
||||
|
||||
export type Computed<TValue> = (...args: any[]) => TValue;
|
||||
|
||||
export function createComputed<TValue>(
|
||||
factory: Computed<TValue>,
|
||||
owner?: any,
|
||||
): Computed<TValue> {
|
||||
let last: TValue;
|
||||
const event = new FlagDispatcher();
|
||||
const context: DependencyContext = {
|
||||
dependencies: new Set<Subscribable<void>>(),
|
||||
handler: () => event.raise(),
|
||||
owner,
|
||||
};
|
||||
|
||||
const handler = <Computed<TValue>>function handler(...args: any[]) {
|
||||
@@ -24,7 +27,15 @@ export function createComputed<TValue>(
|
||||
context.dependencies.clear();
|
||||
context.stack = new Error().stack;
|
||||
startCollecting(context);
|
||||
last = factory(...args);
|
||||
try {
|
||||
last = factory(...args);
|
||||
} catch (e) {
|
||||
useLogger().error({
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
inspect: context.owner?.key,
|
||||
});
|
||||
}
|
||||
finishCollecting(context);
|
||||
}
|
||||
event.reset();
|
||||
|
||||
@@ -7,11 +7,13 @@ import {
|
||||
InterpolationFunction,
|
||||
} from '../tweening';
|
||||
import {ThreadGenerator} from '../threading';
|
||||
import {useLogger} from './useProject';
|
||||
|
||||
export interface DependencyContext {
|
||||
dependencies: Set<Subscribable<void>>;
|
||||
handler: EventHandler<void>;
|
||||
stack?: string;
|
||||
owner?: any;
|
||||
}
|
||||
|
||||
export type SignalValue<TValue> = TValue | (() => TValue);
|
||||
@@ -121,6 +123,7 @@ export interface PromiseHandle<T> {
|
||||
promise: Promise<T>;
|
||||
value: T;
|
||||
stack?: string;
|
||||
owner?: any;
|
||||
}
|
||||
|
||||
export function collectPromise<T>(
|
||||
@@ -130,10 +133,13 @@ export function collectPromise<T>(
|
||||
const handle: PromiseHandle<T> = {
|
||||
promise,
|
||||
value: initialValue,
|
||||
stack: collectionStack[0].stack,
|
||||
stack: collectionStack[0]?.stack,
|
||||
};
|
||||
|
||||
const context = collectionStack.at(-2);
|
||||
if (context) {
|
||||
handle.owner = context.owner;
|
||||
}
|
||||
promise.then(value => {
|
||||
handle.value = value;
|
||||
context?.handler();
|
||||
@@ -164,6 +170,7 @@ export function createSignal<TValue, TReturn = void>(
|
||||
const context: DependencyContext = {
|
||||
dependencies: new Set<Subscribable<void>>(),
|
||||
handler: () => event.raise(),
|
||||
owner: setterReturn,
|
||||
};
|
||||
|
||||
function set(value: SignalValue<TValue>) {
|
||||
@@ -192,7 +199,15 @@ export function createSignal<TValue, TReturn = void>(
|
||||
context.dependencies.clear();
|
||||
context.stack = new Error().stack;
|
||||
startCollecting(context);
|
||||
last = current();
|
||||
try {
|
||||
last = current();
|
||||
} catch (e) {
|
||||
useLogger().error({
|
||||
message: e.message,
|
||||
stack: e.stack,
|
||||
inspect: context.owner?.key,
|
||||
});
|
||||
}
|
||||
finishCollecting(context);
|
||||
}
|
||||
event.reset();
|
||||
|
||||
Reference in New Issue
Block a user