From 917edbacada5db130f3414149c3febcfd12ee4e0 Mon Sep 17 00:00:00 2001 From: veeck Date: Sun, 12 Feb 2023 16:22:08 +0100 Subject: [PATCH 1/6] Convert callback in start to async --- js/app.js | 84 +++++++++++++++---------------- js/electron.js | 4 +- serveronly/index.js | 2 +- tests/e2e/helpers/global-setup.js | 4 +- 4 files changed, 45 insertions(+), 49 deletions(-) diff --git a/js/app.js b/js/app.js index 943d9c6197..38aa6d948f 100644 --- a/js/app.js +++ b/js/app.js @@ -53,6 +53,9 @@ function App() { /** * Loads the config file. Combines it with the defaults and returns the config + * + * @async + * @returns {Promise} the loaded config or the defaults if something goes wrong */ async function loadConfig() { Log.log("Loading config ..."); @@ -115,8 +118,7 @@ function App() { fs.accessSync(configFilename, fs.F_OK); const c = require(configFilename); checkDeprecatedOptions(c); - const config = Object.assign(defaults, c); - return config; + return Object.assign(defaults, c); } catch (e) { if (e.code === "ENOENT") { Log.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration.")); @@ -125,8 +127,9 @@ function App() { } else { Log.error(Utils.colors.error(`WARNING! Could not load config file. Starting with default configuration. Error found: ${e}`)); } - return defaults; } + + return defaults; } /** @@ -261,56 +264,53 @@ function App() { * It loads the config, then it loads all modules. When it's done it * executes the callback with the config as argument. * - * @param {Function} callback Function to be called after start + * @async + * @returns {Promise} the config used */ - this.start = function (callback) { - loadConfig().then((c) => { - config = c; + this.start = async function () { + config = await loadConfig(); - Log.setLogLevel(config.logLevel); + Log.setLogLevel(config.logLevel); - let modules = []; + let modules = []; - for (const module of config.modules) { - if (!modules.includes(module.module) && !module.disabled) { - modules.push(module.module); - } + for (const module of config.modules) { + if (!modules.includes(module.module) && !module.disabled) { + modules.push(module.module); } + } - loadModules(modules, async function () { - httpServer = new Server(config); - const { app, io } = await httpServer.open(); - Log.log("Server started ..."); - - const nodePromises = []; - for (let nodeHelper of nodeHelpers) { - nodeHelper.setExpressApp(app); - nodeHelper.setSocketIO(io); - - try { - nodePromises.push(nodeHelper.start()); - } catch (error) { - Log.error(`Error when starting node_helper for module ${nodeHelper.name}:`); - Log.error(error); - } + loadModules(modules, async function () { + httpServer = new Server(config); + const { app, io } = await httpServer.open(); + Log.log("Server started ..."); + + const nodePromises = []; + for (let nodeHelper of nodeHelpers) { + nodeHelper.setExpressApp(app); + nodeHelper.setSocketIO(io); + + try { + nodePromises.push(nodeHelper.start()); + } catch (error) { + Log.error(`Error when starting node_helper for module ${nodeHelper.name}:`); + Log.error(error); } + } - Promise.allSettled(nodePromises).then((results) => { - // Log errors that happened during async node_helper startup - results.forEach((result) => { - if (result.status === "rejected") { - Log.error(result.reason); - } - }); - - Log.log("Sockets connected & modules started ..."); + Promise.allSettled(nodePromises).then((results) => { + // Log errors that happened during async node_helper startup + results.forEach((result) => { + if (result.status === "rejected") { + Log.error(result.reason); + } }); - }); - if (typeof callback === "function") { - callback(config); - } + Log.log("Sockets connected & modules started ..."); + }); }); + + return config; }; /** diff --git a/js/electron.js b/js/electron.js index 9e4d3f5e24..9e5e3f1815 100644 --- a/js/electron.js +++ b/js/electron.js @@ -177,7 +177,5 @@ app.on("certificate-error", (event, webContents, url, error, certificate, callba // Start the core application if server is run on localhost // This starts all node helpers and starts the webserver. if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].includes(config.address)) { - core.start(function (c) { - config = c; - }); + core.start().then((c) => (config = c)); } diff --git a/serveronly/index.js b/serveronly/index.js index 00d6b64be3..42cfd0496f 100644 --- a/serveronly/index.js +++ b/serveronly/index.js @@ -1,7 +1,7 @@ const app = require("../js/app.js"); const Log = require("logger"); -app.start((config) => { +app.start().then((config) => { const bindAddress = config.address ? config.address : "localhost"; const httpType = config.useHttps ? "https" : "http"; Log.log("\nReady to go! Please point your browser to: " + httpType + "://" + bindAddress + ":" + config.port); diff --git a/tests/e2e/helpers/global-setup.js b/tests/e2e/helpers/global-setup.js index 582ab3ec81..34d3905cbb 100644 --- a/tests/e2e/helpers/global-setup.js +++ b/tests/e2e/helpers/global-setup.js @@ -15,9 +15,7 @@ exports.startApplication = async (configFilename, exec) => { if (exec) exec; global.app = require("app.js"); - return new Promise((resolve) => { - global.app.start(resolve); - }); + return global.app.start(); }; exports.stopApplication = async () => { From eeae07e81d0efc35f476368ca75d351cfe85a664 Mon Sep 17 00:00:00 2001 From: veeck Date: Sun, 12 Feb 2023 22:41:00 +0100 Subject: [PATCH 2/6] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 413afd5452..d80ad2c52d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ _This release is scheduled to be released on 2023-04-01._ - Changed updatenotification module for MagicMirror repo only: Send only notifications for `master` if there is a tag on a newer commit - Update dates in Calendar widgets every minute - Cleanup jest coverage for patches +- Convert more callbacks to async/awaits - Update `stylelint` dependencies, switch to `stylelint-config-standard` and handle `stylelint` issues - Convert translator callbacks to async/await From 4df9829574bc979ceca0ad2daed2ecd7210625c4 Mon Sep 17 00:00:00 2001 From: veeck Date: Sun, 12 Feb 2023 22:48:33 +0100 Subject: [PATCH 3/6] Update jsdoc --- js/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/app.js b/js/app.js index 38aa6d948f..2277ed7a96 100644 --- a/js/app.js +++ b/js/app.js @@ -261,8 +261,7 @@ function App() { /** * Start the core app. * - * It loads the config, then it loads all modules. When it's done it - * executes the callback with the config as argument. + * It loads the config, then it loads all modules. * * @async * @returns {Promise} the config used From 1753914bc61883197c6134ed967d6da973c9aaee Mon Sep 17 00:00:00 2001 From: veeck Date: Sun, 12 Feb 2023 22:58:28 +0100 Subject: [PATCH 4/6] Convert callback in stop to async --- js/app.js | 44 ++++++++++++++++++++++++------- js/electron.js | 9 ++++--- tests/e2e/helpers/global-setup.js | 10 +++---- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/js/app.js b/js/app.js index 2277ed7a96..bc1987ad35 100644 --- a/js/app.js +++ b/js/app.js @@ -318,15 +318,39 @@ function App() { * * Added to fix #1056 * - * @param {Function} callback Function to be called after the app has stopped + * @returns {Promise} A promise that is resolved when all node_helpers and + * the http server has been closed */ - this.stop = function (callback) { - for (const nodeHelper of nodeHelpers) { - if (typeof nodeHelper.stop === "function") { - nodeHelper.stop(); + this.stop = async function () { + const nodePromises = []; + for (let nodeHelper of nodeHelpers) { + try { + if (typeof nodeHelper.stop === "function") { + nodePromises.push(nodeHelper.stop()); + } + } catch (error) { + Log.error(`Error when stopping node_helper for module ${nodeHelper.name}:`); + console.error(error); } } - httpServer.close().then(callback); + + const results = await Promise.allSettled(nodePromises); + + // Log errors that happened during async node_helper stopping + results.forEach((result) => { + if (result.status === "rejected") { + Log.error(result.reason); + } + }); + Log.log("Node_helpers stopped ..."); + + // To be able to stop the app even if it hasn't been started (when + // running with Electron against another server) + if (!httpServer) { + return Promise.resolve(); + } + + return httpServer.close(); }; /** @@ -336,12 +360,12 @@ function App() { * Note: this is only used if running `server-only`. Otherwise * this.stop() is called by app.on("before-quit"... in `electron.js` */ - process.on("SIGINT", () => { + process.on("SIGINT", async () => { Log.log("[SIGINT] Received. Shutting down server..."); setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds - this.stop(); + await this.stop(); process.exit(0); }); @@ -349,12 +373,12 @@ function App() { * Listen to SIGTERM signals so we can stop everything when we * are asked to stop by the OS. */ - process.on("SIGTERM", () => { + process.on("SIGTERM", async () => { Log.log("[SIGTERM] Received. Shutting down server..."); setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds - this.stop(); + await this.stop(); process.exit(0); }); } diff --git a/js/electron.js b/js/electron.js index 9e5e3f1815..13247cea43 100644 --- a/js/electron.js +++ b/js/electron.js @@ -157,18 +157,19 @@ app.on("activate", function () { * Note: this is only used if running Electron. Otherwise * core.stop() is called by process.on("SIGINT"... in `app.js` */ -app.on("before-quit", (event) => { +app.on("before-quit", async (event) => { Log.log("Shutting down server..."); event.preventDefault(); setTimeout(() => { process.exit(0); }, 3000); // Force-quit after 3 seconds. - core.stop(); + await core.stop(); process.exit(0); }); -/* handle errors from self signed certificates */ - +/** + * Handle errors from self-signed certificates + */ app.on("certificate-error", (event, webContents, url, error, certificate, callback) => { event.preventDefault(); callback(true); diff --git a/tests/e2e/helpers/global-setup.js b/tests/e2e/helpers/global-setup.js index 34d3905cbb..93612847e8 100644 --- a/tests/e2e/helpers/global-setup.js +++ b/tests/e2e/helpers/global-setup.js @@ -19,13 +19,11 @@ exports.startApplication = async (configFilename, exec) => { }; exports.stopApplication = async () => { - if (global.app) { - return new Promise((resolve) => { - global.app.stop(resolve); - delete global.app; - }); + if (!global.app) { + return Promise.resolve(); } - return Promise.resolve(); + await global.app.stop(); + delete global.app; }; exports.getDocument = () => { From 9c4fa24789e8b36391b220c9f9435cb70559dea2 Mon Sep 17 00:00:00 2001 From: veeck Date: Sat, 18 Feb 2023 18:15:49 +0100 Subject: [PATCH 5/6] Use await to match other allSettled code --- js/app.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/js/app.js b/js/app.js index bc1987ad35..8a0163338f 100644 --- a/js/app.js +++ b/js/app.js @@ -297,16 +297,16 @@ function App() { } } - Promise.allSettled(nodePromises).then((results) => { - // Log errors that happened during async node_helper startup - results.forEach((result) => { - if (result.status === "rejected") { - Log.error(result.reason); - } - }); + const results = await Promise.allSettled(nodePromises); - Log.log("Sockets connected & modules started ..."); + // Log errors that happened during async node_helper startup + results.forEach((result) => { + if (result.status === "rejected") { + Log.error(result.reason); + } }); + + Log.log("Sockets connected & modules started ..."); }); return config; @@ -342,6 +342,7 @@ function App() { Log.error(result.reason); } }); + Log.log("Node_helpers stopped ..."); // To be able to stop the app even if it hasn't been started (when From 0afd71522f576a82a4173732098b9d936f053c22 Mon Sep 17 00:00:00 2001 From: veeck Date: Tue, 21 Feb 2023 23:07:23 +0100 Subject: [PATCH 6/6] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d80ad2c52d..bc999007e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,9 +36,9 @@ _This release is scheduled to be released on 2023-04-01._ - Changed updatenotification module for MagicMirror repo only: Send only notifications for `master` if there is a tag on a newer commit - Update dates in Calendar widgets every minute - Cleanup jest coverage for patches -- Convert more callbacks to async/awaits - Update `stylelint` dependencies, switch to `stylelint-config-standard` and handle `stylelint` issues - Convert translator callbacks to async/await +- Convert app-start/-stop callbacks to async/awaits ### Fixed