Skip to content

Commit a565d2a

Browse files
authored
Merge pull request element-hq#978 from vector-im/bwindels/errorboundary
ErrorBoundary utility
2 parents 508d88e + a9a72f8 commit a565d2a

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

src/utils/ErrorBoundary.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright 2023 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
export const ErrorValue = Symbol("ErrorBoundary:Error");
18+
19+
export class ErrorBoundary {
20+
private _error?: Error;
21+
22+
constructor(private readonly errorCallback: (Error) => void) {}
23+
24+
/**
25+
* Executes callback() and then runs errorCallback() on error.
26+
* This will never throw but instead return `errorValue` if an error occured.
27+
*/
28+
try<T>(callback: () => T): T | typeof ErrorValue;
29+
try<T>(callback: () => Promise<T>): Promise<T | typeof ErrorValue> | typeof ErrorValue {
30+
try {
31+
let result: T | Promise<T | typeof ErrorValue> = callback();
32+
if (result instanceof Promise) {
33+
result = result.catch(err => {
34+
this._error = err;
35+
this.errorCallback(err);
36+
return ErrorValue;
37+
});
38+
}
39+
return result;
40+
} catch (err) {
41+
this._error = err;
42+
this.errorCallback(err);
43+
return ErrorValue;
44+
}
45+
}
46+
47+
get error(): Error | undefined {
48+
return this._error;
49+
}
50+
}
51+
52+
export function tests() {
53+
return {
54+
"catches sync error": assert => {
55+
let emitted = false;
56+
const boundary = new ErrorBoundary(() => emitted = true);
57+
const result = boundary.try(() => {
58+
throw new Error("fail!");
59+
});
60+
assert(emitted);
61+
assert.strictEqual(result, ErrorValue);
62+
},
63+
"return value of callback is forwarded": assert => {
64+
let emitted = false;
65+
const boundary = new ErrorBoundary(() => emitted = true);
66+
const result = boundary.try(() => {
67+
return "hello";
68+
});
69+
assert(!emitted);
70+
assert.strictEqual(result, "hello");
71+
},
72+
"catches async error": async assert => {
73+
let emitted = false;
74+
const boundary = new ErrorBoundary(() => emitted = true);
75+
const result = await boundary.try(async () => {
76+
throw new Error("fail!");
77+
});
78+
assert(emitted);
79+
assert.strictEqual(result, ErrorValue);
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)