Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit edb8135

Browse files
authored
fix: restore context when a function run with a given context throws (#727)
PR-URL: #727
1 parent 132db9b commit edb8135

2 files changed

Lines changed: 36 additions & 8 deletions

File tree

src/cls/async-hooks.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,11 @@ export class AsyncHooksCLS<Context extends {}> implements CLS<Context> {
8686
runWithNewContext<T>(fn: Func<T>): T {
8787
const oldContext = this.currentContext.value;
8888
this.currentContext.value = this.defaultContext;
89-
// TODO(kjin) Handle the case where fn throws. Context: PR #708
90-
const res = fn();
91-
this.currentContext.value = oldContext;
92-
return res;
89+
try {
90+
return fn();
91+
} finally {
92+
this.currentContext.value = oldContext;
93+
}
9394
}
9495

9596
bindWithCurrentContext<T>(fn: Func<T>): Func<T> {
@@ -101,10 +102,11 @@ export class AsyncHooksCLS<Context extends {}> implements CLS<Context> {
101102
const contextWrapper: ContextWrapped<Func<T>> = function(this: {}) {
102103
const oldContext = current.value;
103104
current.value = boundContext;
104-
// TODO(kjin) Handle the case where fn throws. Context: PR #708
105-
const res = fn.apply(this, arguments) as T;
106-
current.value = oldContext;
107-
return res;
105+
try {
106+
return fn.apply(this, arguments) as T;
107+
} finally {
108+
current.value = oldContext;
109+
}
108110
};
109111
contextWrapper[WRAPPED] = true;
110112
Object.defineProperty(contextWrapper, 'length', {

test/test-cls.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,32 @@ describe('Continuation-Local Storage', () => {
130130
});
131131
});
132132

133+
it('Corrects context when function run with new context throws', () => {
134+
try {
135+
c.runWithNewContext(() => {
136+
c.setContext('modified');
137+
throw new Error();
138+
});
139+
} catch (e) {
140+
assert.strictEqual(c.getContext(), 'default');
141+
}
142+
});
143+
144+
it('Corrects context when function bound to a context throws', () => {
145+
let runLater = () => {
146+
c.setContext('modified');
147+
throw new Error();
148+
};
149+
c.runWithNewContext(() => {
150+
runLater = c.bindWithCurrentContext(runLater);
151+
});
152+
try {
153+
runLater();
154+
} catch (e) {
155+
assert.strictEqual(c.getContext(), 'default');
156+
}
157+
});
158+
133159
it('Can be used to patch event emitters to propagate context', () => {
134160
const ee = new EventEmitter();
135161
assert.strictEqual(c.getContext(), 'default');

0 commit comments

Comments
 (0)