Skip to content

Commit e3e4fe2

Browse files
committed
♻️ Don't use api decorator for scope.destroy
We were installing an api decorator as the method for implementing the destroy method on scope, but this required recomputing the middleware stack for every scope creation which is totally unecessary work. This just puts it on a prototype method of ScopeInternal. That way, there is one version of the function, and the default api handle, just invokes it straight away.
1 parent 60abb25 commit e3e4fe2

2 files changed

Lines changed: 21 additions & 22 deletions

File tree

lib/api.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export const api: Apis = {
2828
create() {
2929
throw new TypeError(`no handler for Scope.create()`);
3030
},
31-
*destroy() {},
31+
destroy(scope) {
32+
return (scope as ScopeInternal).destroy();
33+
},
3234
set(scope, context, value) {
3335
return (scope as ScopeInternal).contexts[context.name] = value;
3436
},

lib/scope-internal.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export function buildScopeInternal(
3333
): [ScopeInternal, () => Operation<void>] {
3434
let destructors = new Set<() => Operation<void>>();
3535
let destruction = createFuture<void>();
36+
let signaled = false;
37+
let unbind = parent
38+
? (parent as ScopeInternal).ensure(() => destroy())
39+
: () => {};
3640

3741
let contexts: Record<string, unknown> = Object.create(
3842
parent ? (parent as ScopeInternal).contexts : null,
@@ -82,21 +86,12 @@ export function buildScopeInternal(
8286
destructors.add(op);
8387
return () => destructors.delete(op);
8488
},
85-
});
86-
87-
scope.set(Priority, scope.expect(Priority) + 1);
88-
scope.set(Children, new Set());
89-
parent?.expect(Children).add(scope);
9089

91-
let destroy = () => api.invoke(scope, "destroy", [scope]);
92-
let unbind = parent
93-
? (parent as ScopeInternal).ensure(() => destroy())
94-
: () => {};
95-
96-
scope.around(api, {
9790
*destroy(): Operation<void> {
98-
destroy = () => destruction.future;
99-
91+
if (signaled) {
92+
return yield* destruction.future;
93+
}
94+
signaled = true;
10095
parent?.expect(Children).delete(scope);
10196
unbind();
10297
let outcome = Ok();
@@ -121,18 +116,20 @@ export function buildScopeInternal(
121116
}
122117

123118
unbox(outcome);
124-
},
125-
}, { at: "min" });
119+
}
120+
});
121+
122+
scope.set(Priority, scope.expect(Priority) + 1);
123+
scope.set(Children, new Set());
124+
parent?.expect(Children).add(scope);
125+
126+
let destroy = () => api.invoke(scope, "destroy", [scope]);
126127

127-
return [scope, () => destroy()];
128+
return [scope, destroy];
128129
}
129130

130131
export interface ScopeInternal extends Scope, AsyncDisposable {
131132
contexts: Record<string, unknown>;
132133
ensure(op: () => Operation<void>): () => void;
133-
reduce<T, TSum>(
134-
context: Context<T>,
135-
fn: (sum: TSum, item: T) => TSum,
136-
initial: TSum,
137-
): TSum;
134+
destroy(): Operation<void>;
138135
}

0 commit comments

Comments
 (0)