Error handling
Oh no! An exception occured. What can you do about it? Well, it depends. ArcOS has error handling measures built-in to prevent it from crashing when you make a mistake. However, there is only so much I can do with the JS engine being the way it is. This page covers some best practices when it comes to error handling.
Starting in ArcOS 7.0.4, the global error handling system for ArcOS has changed. Now, when a file of your app throws an unhandled error (like from a callback), ArcOS will detect that and intercept the system-wide crash by instead crashing the process immediately responsible, making it really hard to unintentionally crash ArcOS using your app's code. However, bugs in first-party apps (the built-in apps of ArcOS) aren't detectable in this way because they're bundled into ArcOS' JS file on build time. Luckily, bug reports still exist!
Callbacks
If you have a callback, like an HTML event or a user preferences update, please make sure to wrap the code inside that callback in a try/catch if it can possibly error. If you don't, ArcOS' global error handling will take over and declare it as a system-wide crash. That's not what you want, is it? Say we have this event listener:
saveButton.addEventListener("click", () => {
this.saveFile();
})
That saveFile
call runs outside the initial scope of the App Renderer's error handling, so an error in this callback will crash ArcOS. This even happens if you wrap the entire addEventListener
call:
try {
saveButton.addEventListener("click", () => {
this.saveFile();
});
} catch (e) {
Debug(`Error: ${e}`);
}
because of the same problem. Instead, you can either wrap the saveFile
call inside of the callback, or you can make sure the saveFile
method correctly handles errors. Say we have the saveFile
method as such:
async saveFile() {
const path = this.path();
const contents = this.contents();
const prog = await this.userDaemon.FileProgress(
{
type: "size",
caption: `Saving file...`,
subtitle: path,
icon: DriveIcon,
},
this.pid
);
await this.fs.writeFile(path, textToBlob(contents), async (progress) => {
await prog.show();
prog.setMax(progress.max);
prog.setDone(progress.value);
});
await prog.stop();
}
Let's wrap the error-prone parts in a try/catch to prevent them from crashing the OS:
async saveFile() {
const path = this.path();
const contents = this.contents();
let prog;
try {
prog = await this.userDaemon.FileProgress(
{
type: "size",
caption: `Saving file...`,
subtitle: path,
icon: DriveIcon,
},
this.pid
);
await this.fs.writeFile(path, textToBlob(contents), async (progress) => {
await prog.show();
prog.setMax(progress.max);
prog.setDone(progress.value);
});
} catch (e) {
this.saveFailed(e); // Gracefully handle the error
}
await prog?.stop();
}
Ofcourse you would ideally display a message box or notification or something to the user to inform them that the operation failed, but hopefully you get the idea.
Last updated