mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-22 19:14:52 +01:00
make it clearer that logAndCatch is probably what you want
This commit is contained in:
parent
dfaaf6d234
commit
bf9c868c8b
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
Ideally, every error that is unexpected and can't be automatically recovered from without degrading the experience is shown in the UI. This is the task of the view model, and you can use `ErrorReportViewModel` for this purpose, a dedicated base view model class. It exposes a child view model, `ErrorViewModel`, when `reportError` is called which can be paired with `ErrorView` in the view to present an error message from which debug logs can also be sent.
|
Ideally, every error that is unexpected and can't be automatically recovered from without degrading the experience is shown in the UI. This is the task of the view model, and you can use `ErrorReportViewModel` for this purpose, a dedicated base view model class. It exposes a child view model, `ErrorViewModel`, when `reportError` is called which can be paired with `ErrorView` in the view to present an error message from which debug logs can also be sent.
|
||||||
|
|
||||||
Methods on classes from the `matrix` layer can often throw errors and those errors should be caught in the view model and reported with `reportError`. As a convenience method, there is also `logAndCatch` when inheriting from `ErrorReportViewModel` which combines logging and error reporting; it calls a callback within a log item and also a try catch that reports the error.
|
Methods on classes from the `matrix` layer can often throw errors and those errors should be caught in the view model and reported to the UI. When inheriting from `ErrorReportViewModel`, there is the low-level `reportError` method, but typically you'd use the convenience method `logAndCatch`. The latter makes it easy to get both error handlikng and logging right. You would typically use `logAndCatch` for every public method in the view model (e.g methods called from the view or from the parent view model). It calls a callback within a log item and also a try catch that reports the error.
|
||||||
|
|
||||||
## Sync errors & ErrorBoundary
|
## Sync errors & ErrorBoundary
|
||||||
|
|
||||||
There are some errors that are thrown during background processes though, most notably the sync loop. These processes are not triggered by the view model directly, and hence there is not always a method call they can wrap in a try/catch. For this, there is the `ErrorBoundary` utility class. Since almost all aspects of the client can be updated through the sync loop, it is also not too helpful if there is only one try/catch around the whole sync and we stop sync if something goes wrong.
|
There are some errors that are thrown during background processes though, most notably the sync loop. These processes are not triggered by the view model directly, and hence there is not always a method call they can wrap in a try/catch. For this, there is the `ErrorBoundary` utility class. Since almost all aspects of the client can be updated through the sync loop, it is also not too helpful if there is only one try/catch around the whole sync and we stop sync if something goes wrong.
|
||||||
|
|
||||||
Instead, it's more helpful to split up the error handling into different scopes, where errors are stored and not rethrown when leaving the scope. One example is to have a scope per room. In this way, we can isolate an error occuring during sync to a specific room, and report it in the UI of that room.
|
Instead, it's more helpful to split up the error handling into different scopes, where errors are stored and not rethrown when leaving the scope. One example is to have a scope per room. In this way, we can isolate an error occuring during sync to a specific room, and report it in the UI of that room. This is typically where you would use `reportError` from `ErrorReportViewModel` rather than `logAndCatch`. You observe changes from your model in the view model (see docs on updates), and if the `error` property is set (by the `ErrorBoundary`), you call reportError with it. You can do this repeatedly without problems, if the same error is already reported, it's a No-Op.
|
||||||
|
|
||||||
|
### `writeSync` and preventing data loss when dealing with errors.
|
||||||
|
|
||||||
There is an extra complication though. The `writeSync` sync lifecycle step should not swallow any errors, or data loss can occur. This is because the whole `writeSync` lifecycle step writes all changes (for all rooms, the session, ...) for a sync response in one transaction (including the sync token), and aborts the transaction and stops sync if there is an error thrown during this step. So if there is an error in `writeSync` of a given room, it's fair to assume not all changes it was planning to write were passed to the transaction, as it got interrupted by the exception. Therefore, if we would swallow the error, data loss can occur as we'd not get another chance to write these changes to disk as we would have advanced the sync token. Therefore, code in the `writeSync` lifecycle step should be written defensively but always throw.
|
There is an extra complication though. The `writeSync` sync lifecycle step should not swallow any errors, or data loss can occur. This is because the whole `writeSync` lifecycle step writes all changes (for all rooms, the session, ...) for a sync response in one transaction (including the sync token), and aborts the transaction and stops sync if there is an error thrown during this step. So if there is an error in `writeSync` of a given room, it's fair to assume not all changes it was planning to write were passed to the transaction, as it got interrupted by the exception. Therefore, if we would swallow the error, data loss can occur as we'd not get another chance to write these changes to disk as we would have advanced the sync token. Therefore, code in the `writeSync` lifecycle step should be written defensively but always throw.
|
||||||
|
@ -32,6 +32,10 @@ export class ErrorReportViewModel<O extends Options = Options> extends ViewModel
|
|||||||
return this._errorViewModel;
|
return this._errorViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Typically you'd want to use `logAndCatch` when implementing a view model method.
|
||||||
|
* Use `reportError` when showing errors on your model that were set by
|
||||||
|
* background processes using `ErrorBoundary` or you have some other
|
||||||
|
* special low-level need to write your try/catch yourself. */
|
||||||
protected reportError(error: Error) {
|
protected reportError(error: Error) {
|
||||||
if (this._errorViewModel?.error === error) {
|
if (this._errorViewModel?.error === error) {
|
||||||
return;
|
return;
|
||||||
@ -47,6 +51,9 @@ export class ErrorReportViewModel<O extends Options = Options> extends ViewModel
|
|||||||
this.emitChange("errorViewModel");
|
this.emitChange("errorViewModel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Combines logging and error reporting in one method.
|
||||||
|
* Wrap the implementation of public view model methods
|
||||||
|
* with this to ensure errors are logged and reported.*/
|
||||||
protected logAndCatch<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, errorValue: T = undefined as unknown as T): T {
|
protected logAndCatch<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, errorValue: T = undefined as unknown as T): T {
|
||||||
try {
|
try {
|
||||||
let result = this.logger.run(labelOrValues, callback);
|
let result = this.logger.run(labelOrValues, callback);
|
||||||
|
Loading…
Reference in New Issue
Block a user