├── .editorconfig ├── .gitattributes ├── .gitignore ├── background ├── index.html └── index.js ├── index.js ├── license ├── package.json ├── readme.md ├── renderer ├── index.css ├── index.html └── index.js └── shared └── task.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | -------------------------------------------------------------------------------- /background/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /background/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { ipcRenderer } = require('electron'); 3 | const task = require('../shared/task'); 4 | 5 | window.onload = function () { 6 | ipcRenderer.on('background-start', (startTime) => { 7 | ipcRenderer.send('background-response', { 8 | result: task(), 9 | startTime: startTime 10 | }); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { app, BrowserWindow, ipcMain } = require('electron'); 3 | 4 | // prevent window being garbage collected 5 | let mainWindow; 6 | let backgroundWindow; 7 | 8 | function onClosed() { 9 | // dereference the window 10 | // for multiple windows store them in an array 11 | mainWindow = null; 12 | backgroundWindow = null; 13 | } 14 | 15 | function createMainWindow() { 16 | const win = new BrowserWindow({ 17 | width: 800, 18 | height: 600 19 | }); 20 | 21 | win.loadURL(`file://${__dirname}/renderer/index.html`); 22 | win.on('closed', onClosed); 23 | 24 | return win; 25 | } 26 | 27 | function createBackgroundWindow() { 28 | const win = new BrowserWindow({ 29 | show: false 30 | }); 31 | 32 | win.loadURL(`file://${__dirname}/background/index.html`); 33 | 34 | return win; 35 | } 36 | 37 | app.on('window-all-closed', () => { 38 | if (process.platform !== 'darwin') { 39 | app.quit(); 40 | } 41 | }); 42 | 43 | app.on('activate-with-no-open-windows', () => { 44 | if (!mainWindow) { 45 | mainWindow = createMainWindow(); 46 | } 47 | }); 48 | 49 | app.on('ready', () => { 50 | mainWindow = createMainWindow(); 51 | backgroundWindow = createBackgroundWindow(); 52 | }); 53 | 54 | ipcMain.on('background-response', (event, payload) => mainWindow.webContents.send('background-response', payload)); 55 | 56 | ipcMain.on('background-start', (event, payload) => backgroundWindow.webContents.send('background-start', payload)); 57 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) John Haley (http://johnhaley.io) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-background-task-app", 3 | "productName": "electron-background-task-app", 4 | "version": "0.0.0", 5 | "description": "Shows off how to run a task in the background", 6 | "license": "MIT", 7 | "repository": "johnhaley81/electron-background-task-app", 8 | "author": { 9 | "name": "John Haley", 10 | "email": "john@haley.io", 11 | "url": "johnhaley.io" 12 | }, 13 | "engines": { 14 | "node": ">=4" 15 | }, 16 | "electronVersion": "1.0.1", 17 | "scripts": { 18 | "test": "xo", 19 | "start": "electron .", 20 | "build": "electron-packager . $npm_package_productName --out=dist --ignore='^/dist$' --prune --asar --all --version=$npm_package_electronVersion" 21 | }, 22 | "files": [ 23 | "index.js", 24 | "index.html", 25 | "index.css" 26 | ], 27 | "keywords": [ 28 | "electron-app" 29 | ], 30 | "dependencies": { 31 | "electron-debug": "^0.3.0" 32 | }, 33 | "devDependencies": { 34 | "electron-packager": "^5.0.0", 35 | "electron-prebuilt": "1.0.1", 36 | "spectron": "^3.0.0", 37 | "xo": "^0.10.0" 38 | }, 39 | "xo": { 40 | "esnext": true, 41 | "envs": [ 42 | "node", 43 | "browser" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # electron-background-task-app 2 | 3 | > This app shows off how to run a task in the background of an electron app. 4 | 5 | 6 | ## Dev 7 | 8 | ``` 9 | $ npm install 10 | ``` 11 | 12 | ### Run 13 | 14 | ``` 15 | $ npm start 16 | ``` 17 | 18 | ### Build 19 | 20 | ``` 21 | $ npm run build 22 | ``` 23 | 24 | Builds the app for OS X, Linux, and Windows, using [electron-packager](https://github.com/maxogden/electron-packager). 25 | 26 | 27 | ## License 28 | 29 | MIT © [John Haley](http://johnhaley.io) 30 | -------------------------------------------------------------------------------- /renderer/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | font-family: -apple-system, 'Helvetica Neue', Helvetica, sans-serif; 9 | } 10 | 11 | header { 12 | position: absolute; 13 | width: 500px; 14 | height: 250px; 15 | top: 50%; 16 | left: 50%; 17 | margin-top: -225px; 18 | margin-left: -250px; 19 | text-align: center; 20 | } 21 | 22 | header h1 { 23 | font-size: 60px; 24 | font-weight: 100; 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | header div { 30 | margin: 20px; 31 | font-size: 15px; 32 | } 33 | 34 | a { 35 | box-shadow:inset 0px 1px 0px 0px #ffffff; 36 | background:linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%); 37 | border-radius:6px; 38 | border:1px solid #dcdcdc; 39 | display:inline-block; 40 | cursor:pointer; 41 | color:#666666; 42 | font-family: -apple-system, 'Helvetica Neue', Helvetica, sans-serif;; 43 | font-size:15px; 44 | font-weight:bold; 45 | padding:6px 24px; 46 | text-decoration:none; 47 | text-shadow:0px 1px 0px #ffffff; 48 | } 49 | a:hover { 50 | background:linear-gradient(to bottom, #f6f6f6 5%, #ffffff 100%); 51 | } 52 | a:active { 53 | position:relative; 54 | top:1px; 55 | } 56 | -------------------------------------------------------------------------------- /renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Electron background task runner 6 | 7 | 8 | 9 | 10 |
11 |
12 |

Lets run some long running task and watch what it does to the UI

13 |
14 | 15 |
16 | Renderer process 17 | Background process 18 |
19 |
20 |
21 | Waiting... 22 |
23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /renderer/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { ipcRenderer } = require('electron'); 3 | const task = require('../shared/task'); 4 | 5 | window.onload = function () { 6 | setInterval(() => { 7 | const progressBar = document.getElementById('progress-bar'); 8 | const maxValue = parseInt(progressBar.getAttribute('max'), 10); 9 | let nextValue = parseInt(progressBar.getAttribute('value'), 10) + 1; 10 | 11 | if (nextValue > maxValue) { 12 | nextValue = 0; 13 | } 14 | 15 | progressBar.setAttribute('value', nextValue); 16 | }, 25); 17 | 18 | function startProcess() { 19 | document.getElementById('status').textContent = 'Started!'; 20 | } 21 | 22 | function finishProcess(result, timeElapsed) { 23 | document.getElementById('status').textContent = 24 | 'Finished with a result of: ' + 25 | result + 26 | ' in ' + 27 | (timeElapsed / 1000) + 28 | ' seconds'; 29 | } 30 | 31 | const rendererButton = document.getElementById('in-renderer'); 32 | 33 | rendererButton.onclick = function longRunningRendererTask() { 34 | const startTime = new Date(); 35 | 36 | // Note that the UI won't update with this call since we're stuck in a JavaScript process and the UI is 37 | // unresponsive until this loop finishes. The div will go straight to finished. 38 | startProcess(); 39 | 40 | finishProcess(task(), new Date() - startTime); 41 | }; 42 | 43 | const backgroundButton = document.getElementById('in-background'); 44 | 45 | backgroundButton.onclick = function longRunningBackgroundTask() { 46 | // We have to cast to a number because crossing the IPC boundary will convert the Date object to an empty object. 47 | // Error, Date and native objects won't be able to be passed around via IPC. 48 | const backgroundStartTime = +new Date(); 49 | 50 | startProcess(); 51 | ipcRenderer.send('background-start', backgroundStartTime); 52 | } 53 | 54 | ipcRenderer.on('background-response', (event, payload) => { 55 | finishProcess(payload.result, new Date() - payload.startTime); 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /shared/task.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This module is instantiated twice. Once in the background process and once in the renderer process. They can't 4 | // share data other than through IPC. 5 | 6 | module.exports = function task() { 7 | let result = 0; 8 | 9 | for (let i = 0; i < 100000000; i++) { 10 | result += i; 11 | } 12 | 13 | return result; 14 | }; 15 | --------------------------------------------------------------------------------