From af1bb54868d147bd2d67b8b157c3376bd965ffc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E4=B8=80=E4=BA=8C?= Date: Sat, 7 Aug 2021 22:00:19 +0800 Subject: [PATCH] refactor: use try catch around all `webContent.load(url)` --- localization/locales/zh_CN/translation.json | 4 ++- src/constants/parameters.ts | 2 +- src/services/view/error.ts | 9 +++++ src/services/view/index.ts | 40 ++++++++++++++------- src/services/view/setupViewEventHandlers.ts | 12 +++---- 5 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 src/services/view/error.ts diff --git a/localization/locales/zh_CN/translation.json b/localization/locales/zh_CN/translation.json index 0d7d0800..0365f73a 100644 --- a/localization/locales/zh_CN/translation.json +++ b/localization/locales/zh_CN/translation.json @@ -315,7 +315,9 @@ "InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 初始化同步到云端的笔记仓库需要你选择一个云端的 git 仓库地址,还有提供相应云服务的认证凭证,然而目前没有获得这些信息。", "MainWindowMissing": "E-7 程序无法获取主窗口信息,无法正常运行。", "WorkspaceFailedToLoadError": "E-8 工作区加载失败错误", - "WorkspaceFailedToLoadErrorDescription": "E-8 工作区对应的Wiki网页加载失败了,原因有很多,但基本是因为程序Bug" + "WorkspaceFailedToLoadErrorDescription": "E-8 工作区对应的Wiki网页加载失败了,原因有很多,但基本是因为程序Bug", + "ViewLoadUrlError": "E-9 网页加载失败错误", + "ViewLoadUrlErrorDescription": "E-9 工作区对应的Wiki网页加载失败了,但即将重试" }, "Loading": "加载中", "Yes": "是的", diff --git a/src/constants/parameters.ts b/src/constants/parameters.ts index e6674746..1cc56510 100644 --- a/src/constants/parameters.ts +++ b/src/constants/parameters.ts @@ -1 +1 @@ -export const LOAD_VIEW_MAX_RETRIES = 3; +export const LOAD_VIEW_MAX_RETRIES = 10; diff --git a/src/services/view/error.ts b/src/services/view/error.ts new file mode 100644 index 00000000..8210d44a --- /dev/null +++ b/src/services/view/error.ts @@ -0,0 +1,9 @@ +import i18n from '@services/libs/i18n'; + +export class ViewLoadUrlError extends Error { + constructor(initialUrl: string, retryTimes?: number, additionalMessage = '') { + super(); + this.name = i18n.t('Error.ViewLoadUrlError'); + this.message = `${i18n.t('Error.ViewLoadUrlErrorDescription')} initialUrl: ${initialUrl}, retryTimes: ${retryTimes ?? 'undefined'} ${additionalMessage}`; + } +} diff --git a/src/services/view/index.ts b/src/services/view/index.ts index cea9afc7..31791793 100644 --- a/src/services/view/index.ts +++ b/src/services/view/index.ts @@ -22,6 +22,7 @@ import { IViewService } from './interface'; import { SupportedStorageServices } from '@services/types'; import { getLocalHostUrlWithActualIP, replaceUrlPortWithSettingPort } from '@services/libs/url'; import { logger } from '@services/libs/log'; +import { ViewLoadUrlError } from './error'; @injectable() export class View implements IViewService { @@ -251,18 +252,33 @@ export class View implements IViewService { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing replaceUrlPortWithSettingPort((rememberLastPageVisited && workspace.lastUrl) || workspace.homeUrl, workspace.port), ); - setupViewEventHandlers(view, browserWindow, { shouldPauseNotifications: this.shouldPauseNotifications, workspace, sharedWebPreferences, initialUrl }); - // try catch loadUrl, other wise it will throw unhandled promise rejection Error: ERR_CONNECTION_REFUSED (-102) loading 'http://localhost:5212/ - // we will set `didFailLoadErrorMessage`, and `didFailLoadTimes < LOAD_VIEW_MAX_RETRIES` in `setupViewEventHandlers`, it will set didFailLoadErrorMessage, and we throw actuarial error after that - try { - await view.webContents.loadURL(initialUrl); - const unregisterContextMenu = await this.menuService.initContextMenuForWindowWebContents(view.webContents); - view.webContents.on('destroyed', () => { - unregisterContextMenu(); - }); - } catch (error) { - logger.error(`Initial view.webContents.loadURL("${initialUrl}") failed: ${error.message}, but may retry later`); - } + /** + * Try catch loadUrl, other wise it will throw unhandled promise rejection Error: ERR_CONNECTION_REFUSED (-102) loading 'http://localhost:5212/ + * We will set `didFailLoadErrorMessage`, and `didFailLoadTimes < LOAD_VIEW_MAX_RETRIES` in `setupViewEventHandlers`, it will set didFailLoadErrorMessage, and we throw actuarial error after that + */ + const loadInitialUrlWithCatch = async (): Promise => { + try { + await view.webContents.loadURL(initialUrl); + const unregisterContextMenu = await this.menuService.initContextMenuForWindowWebContents(view.webContents); + view.webContents.on('destroyed', () => { + unregisterContextMenu(); + }); + } catch (error) { + let didFailLoadTimes = 0; + try { + const workspaceMetaData = await this.workspaceService.getMetaData(workspace.id); + didFailLoadTimes = workspaceMetaData.didFailLoadTimes ?? 0; + } catch {} + logger.error(new ViewLoadUrlError(initialUrl, didFailLoadTimes, `${(error as Error).message} ${(error as Error).stack ?? ''}`)); + } + }; + setupViewEventHandlers(view, browserWindow, { + shouldPauseNotifications: this.shouldPauseNotifications, + workspace, + sharedWebPreferences, + loadInitialUrlWithCatch, + }); + await loadInitialUrlWithCatch(); } public getView = (id: string): BrowserView => this.views[id]; diff --git a/src/services/view/setupViewEventHandlers.ts b/src/services/view/setupViewEventHandlers.ts index bcbebfda..0bc92c79 100644 --- a/src/services/view/setupViewEventHandlers.ts +++ b/src/services/view/setupViewEventHandlers.ts @@ -25,7 +25,7 @@ export interface IViewContext { workspace: IWorkspace; shouldPauseNotifications: boolean; sharedWebPreferences: BrowserWindowConstructorOptions['webPreferences']; - initialUrl: string; + loadInitialUrlWithCatch: () => Promise; } export interface IViewMeta { @@ -38,7 +38,7 @@ export interface IViewMeta { export default function setupViewEventHandlers( view: BrowserView, browserWindow: BrowserWindow, - { workspace, sharedWebPreferences, initialUrl }: IViewContext, + { workspace, sharedWebPreferences, loadInitialUrlWithCatch }: IViewContext, ): void { // metadata and state about current BrowserView const viewMeta: IViewMeta = { @@ -128,8 +128,8 @@ export default function setupViewEventHandlers( await workspaceService.updateMetaData(workspace.id, { didFailLoadTimes: didFailLoadTimes + 1, }); - await view.webContents.loadURL(initialUrl); - }, 200); + await loadInitialUrlWithCatch(); + }, 1000); return; } await workspaceService.updateMetaData(workspace.id, { @@ -143,8 +143,8 @@ export default function setupViewEventHandlers( } // edge case to handle failed auth, use setTimeout to prevent infinite loop if (errorCode === -300 && view.webContents.getURL().length === 0 && workspaceObject.homeUrl.startsWith('http')) { - setTimeout(() => { - void view.webContents.loadURL(getLocalHostUrlWithActualIP(workspaceObject.homeUrl)); + setTimeout(async () => { + await loadInitialUrlWithCatch(); }, 1000); } });