我正在尝试在我的电子应用程序中实现一种轮询机制,该机制会等到我的 python 服务器启动后再创建浏览器窗口。我需要这个,因为如果服务器启动太慢,那么我必须刷新应用程序才能从服务器获取数据。
我实现了一个 10 秒计时器。当我使用
electron-forge start
启动应用程序时,服务器将在大约 1 秒后启动。您可以在控制台窗口中看到这一点,并且我能够在 chrome 浏览器中成功到达运行状况检查路由(控制台窗口中的信息行是我刷新浏览器以证明我已连接)。由于某种原因,我的轮询机制的实现会不断检查,直到达到 10 秒,然后应用程序退出。
我确实做了一些谷歌搜索,但找不到答案。
问题:为什么Python服务器启动后该机制没有停止?
这是我的
main.ts
和控制台输出:
// Electron modules
import { app, BrowserWindow, ipcMain } from 'electron';
// Built-in modules
import { spawn, exec } from 'child_process';
import path from 'path';
// Extra modules
import getPort, { portNumbers } from 'get-port';
import get from 'axios';
// Function to check if server is running
async function isServerRunning(serverUrl: string) {
try {
const response = await get(serverUrl);
console.log(response);
return response.status === 200;
} catch (error) {
return false;
}
}
// Function to wait for the server to start
async function waitForServer(serverUrl: string) {
const maxRetries = 10; // Set max retries
let attempts = 0;
while (attempts < maxRetries) {
console.log(`Attempt #${attempts}`);
const serverIsRunning = await isServerRunning(serverUrl);
console.log(serverIsRunning);
if (serverIsRunning) {
return true; // Server is up
}
attempts++;
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second before retrying
}
throw new Error('Waiting for server to start.');
}
const createWindow = (port: number) => {
const mainWindow = new BrowserWindow({
frame: false,
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
},
});
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`),
);
}
};
app.whenReady().then(async () => {
const port = 8036;
// Start the flask server in development or production
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
spawn(`python backend/main.py ${port}`, {
detached: true,
shell: true,
stdio: 'inherit',
});
} else {
...
}
try {
await waitForServer(`http://localhost:${port}/health-check`);
createWindow(port);
} catch (error) {
console.error(error);
app.quit();
}
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow(port);
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
});
控制台输出:
✔ Checking your system
✔ Locating application
✔ Loading configuration
✔ Preparing native dependencies [0.2s]
✔ Running generateAssets hook
⠙ [plugin-vite] Launching dev servers for renderer process code
◼ [plugin-vite] Compiling main process code
✔ [plugin-vite] Launching dev servers for renderer process code [0.1s]
⠸ [plugin-vite] Compiling main process code
vite v4.4.9 building for development...
watching for file changes...
vite v4.4.9 building for development...
watching for file changes...
build started...
build started...
✓ 1 modules transformed.
Generated an empty chunk: "preload".
✔ [plugin-vite] Launching dev servers for renderer process code [0.1s]
✔ [plugin-vite] Launching dev servers for renderer process code [0.1s]
⠧ [plugin-vite] Compiling main process code
✔ [plugin-vite] Launching dev servers for renderer process code [0.1s]
✔ [plugin-vite] Compiling main process code [0.8s]
2024-09-26 22:54:06.322 Electron[22787:10970072] WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES.
Attempt #0
false
INFO: Started server process [22790]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8036 (Press CTRL+C to quit)
Attempt #1
false
Attempt #2
false
INFO: 127.0.0.1:53635 - "GET /health-check HTTP/1.1" 200 OK
Attempt #3
false
Attempt #4
false
Attempt #5
false
INFO: 127.0.0.1:53635 - "GET /health-check HTTP/1.1" 200 OK
Attempt #6
false
Attempt #7
false
Attempt #8
false
Attempt #9
false
Error: Waiting for server to start.
at Oo (/repopath/.vite/build/main.js:21:7777)
at async /repopath/.vite/build/main.js:21:8504
我认为有两个问题:
http://localhost:xxx
,而使用 http://127.0.0.1:xxx
。第一个更改不足以使代码正常工作,但在做了更多研究后,似乎
axios
在使用 localhost
时出现问题,因此将其切换为 127.0.0.1
有效。
main.ts
:
import { app, BrowserWindow, ipcMain } from 'electron';
import { spawn, exec } from 'child_process';
import path from 'path';
import axios from 'axios';
const PORT = 8036;
// Function to check if python server is running
async function isServerRunning(url: string) {
try {
const response = await axios.get(url);
return response.status === 200;
} catch (error) {
return false;
}
}
// Function to ping server to see if it is running
async function waitForServer(url: string) {
const maxRetries = 20; // Set max retries
let attempts = 0;
while (attempts < maxRetries) {
console.log(`Attempt #${attempts + 1}`);
const isRunning = await isServerRunning(url);
if (isRunning) {
return true;
}
attempts++;
await new Promise((resolve) => setTimeout(resolve, 1000));
}
throw new Error('Unable to connect to server.');
}
// Function to start server in dev or production
async function startServer() {
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
spawn('python backend/main.py', {
detached: true,
shell: true,
stdio: 'inherit',
});
} else {
...
}
}
// Shut down server and electron
const shutdown = () => {
// Kill app executable if production
if (!MAIN_WINDOW_VITE_DEV_SERVER_URL) {
const killcmd = {
darwin: 'killall App',
linux: '',
win32: 'taskkill /f /t /im App.exe',
aix: undefined,
android: undefined,
cygwin: undefined,
freebsd: undefined,
haiku: undefined,
netbsd: undefined,
openbsd: undefined,
sunos: undefined,
}[process.platform];
exec(killcmd, (err, stdout, stderr) => {
if (err) {
console.log(err);
return;
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
});
}
axios
.get(`http://127.0.0.1:${PORT}/quit`)
.then(() => app.quit())
.catch(() => app.quit());
};
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
frame: false,
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
},
});
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(
path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`),
);
}
};
app.whenReady().then(async () => {
await startServer();
try {
await waitForServer(`http://127.0.0.1:${PORT}/health-check`);
createWindow();
} catch (error) {
console.error(error);
app.quit();
}
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
shutdown();
}
});
});