In this Electron documentation page, they recommend that to restart an application, one should execute app.quit or app.exit after the call to app.relaunch:
Note that this method does not quit the app when executed, you have to call app.quit or app.exit after calling app.relaunch to make the app restart.
However after experimenting I found that the order doesn't seem to actually matter. (See my example below.)
I know that app.quit and app.exit are not quite the same. The former can be interrupted and will trigger some events while the latter will force the app to exit without triggering any events or allowing the app to cancel the action.
Question: assuming that it is always ok to force the app to exit and that we don't have any tasks to perform before the app exits, is there:
A reason to prefer app.quit or app.exit?
A reason why one must run app.quit or app.exit after app.relaunch?
Here's a very simple Electron app:
package.json
{
"name": "burrito",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^4"
}
}
main.js
const {app, BrowserWindow, Menu} = require('electron');
let mainWindow;
app.on('ready', () => {
Menu.setApplicationMenu(
Menu.buildFromTemplate([
{role: 'appMenu', submenu: [
{label: 'relaunch(); exit()', click() {
app.relaunch();
app.exit();
}},
{label: 'relaunch(); quit()', click() {
app.relaunch();
app.quit();
}},
{type: 'separator'},
{label: 'exit(); relaunch()', click() {
app.exit();
app.relaunch();
}},
{label: 'quit(); relaunch()', click() {
app.quit();
app.relaunch();
}}
]}
])
);
mainWindow = new BrowserWindow({width: 640, height: 480});
mainWindow.loadFile('index.html');
});
Producing the following application menu:
A click on any of the menu item will produce the same result: the app quits then restarts.
The proper way to restart an electron app is:
app.relaunch()
app.exit()
See official documentation.
app.relaunch
According to app.relaunch docs:
Relaunches the app when current instance exits.
(...)
Note that this method does not quit the app when executed, you have to call app.quit or app.exit after calling app.relaunch to make the app restart.
When app.relaunch is called for multiple times, multiple instances will be started after current instance exited.
We can assume that:
Calling app.relaunch() only won't do anything (until the user closes the application, then it will restart);
You can call app.quit() or app.exit() in order to close the application, then it will restart because you already called app.relaunch().
app.quit
Citing the docs again:
Try to close all windows. The before-quit event will be emitted first. If all windows are successfully closed, the will-quit event will be emitted and by default the application will terminate.
This method guarantees that all beforeunload and unload event handlers are correctly executed. It is possible that a window cancels the quitting by returning false in the beforeunload event handler.
It means that using app.quit() may or may not terminate the application.
app.exit
The docs states:
All windows will be closed immediately without asking the user, and the before-quit and will-quit events will not be emitted.
So, immediately is a dangerous word. It means that the app will be closed as soon as it is possible, and that it certainly will close, but not instantly.
Should I use app.quit or app.exit?
Both are valid, depends on your use case:
If your user may be on a screen where you have some validations to prevent they from closing the app because of reasons, app.quit is probably better for you. Just be careful to not call app.relaunch more than once (it causes multiple instances to be started after exiting the current instance):
app.relaunch();
app.quit(); // maybe the application will be closed; maybe not
If you need (or want) to close the app immediately in order to apply some setting, for example, no matter what is happening in that moment, you should call app.exit. Doing it you are safe that the app will be closed and relaunched:
app.relaunch();
app.exit(); // the application will be closed as soon as possible
Why should I call app.relaunch first?
Well, because it is logical. See the code below:
app.exit();
app.relaunch();
You're telling: "Hey, Electron, please close my app. Oh, and restart it after closing.". It'll work most of the times, but only because app.exit is slower than app.relaunch to execute. They're not running synchronously.
If, for some reason, Electron terminates your application before knowing it should be relaunched, it would be an unexpected behavior for you, but that's what you told Electron to do.
quit gracefully closes all the windows then exits, compared to exit which simply terminates the application with no regard to anything else, like process.exit in Node. You'll want to use quit in most situations for safety.
It is preferred to call relaunch first to prevent race conditions. This will almost never happen in production because of how the event loop works, but it's just good practice.
Related
I'm experiencing an issue in Electron where window.webContents.send() behaves as expected on the first execution, but after I call it again the function runs twice, then three times, etc.
Here's a stripped back example of one of the menubar functions I'm using to tell the renderer to package data for saving.
Main.js
{
label: 'Save',
click() {
window.webContents.send('myChannel', myArgs);
}
}
Renderer.js
ipcRenderer.once('myChannel', (evt, myArgs) => {
// package data for saving
});
My understanding is that Electron is creating a new event handler each time I click save in the menubar. I can't seem to find any documentation on how to dispose the event handler in the electron docs.
Here's a link to a similar problem. The solution isn't of much help to me, as I need these functions to only be run when they're called from the menubar, not when the app first launches.
Thanks!
While I wrote this answer for StackOverflow I came across the problem that I wanted to execute some code exactly once in Nuxt.js when the server is started (or while the server is starting).
I thought about writing a module or a plugin, but I did not succeed, since both were executed every time I contacted the server, i.e., every time I made a call to localhost:3000/myPage.
Probably I am missing the right module hook or some plugin setting?
Since I could not find any information on this problem in the official documentation, I now came up with a quite ugly solution: I wrote a serverMiddleware that executes some code (the code that should be run only once) and then returns an empty handler - which now adds (little) unnecessary overhead to every request.
My question: What would have been the best way to execute some code exactly once on a Nuxt.js server during/after startup, but not during building?
Minimal ugly working example:
nuxt.config.js:
export default {
...
serverMiddleware: [ "~/serverMiddleware/run-once.js" ]
...
}
~/serverMiddleware/run-once.js:
console.log("Yay, I only run once when the server is started!")
// Since we are a serverMiddleware, we have to return a handler, even if this it does nothing
// I think this is really ugly...
export default function (req, res, next) {
next()
}
PS: I saw questions such as Is it possible for Nuxt JS plugins to only run once? but it seems to not answer my question since the vue-renderer:ssr:prepareContext nuxt hook also executes every time I send a request to the server.
Also, a module such as the following does not work since it is also executed during nuxt build and not only on nuxt dev or nuxt start:
export default function MyModuleThatShouldOnlyRunOnce (_moduleOptions) {
console.log("I run on 'nuxt dev' and 'nuxt start', but sadly also on 'nuxt build' :(");
}
I don't know if this topic is still relevant, but you can solve this using a 'ready' hook:
First create a file like this (./hooks/hooks.js):
export default (nuxtConfig) => ({
ready: () => {
// Execute your code here
}
});
Then add it to your nuxt.config.js:
import hooks from './hooks/hooks';
export default {
// Other stuff
hooks: hooks(this),
// Other stuff
}
I have a problem implementing beacons in my app. I am using the library react-native-beacons-manager, but I think is a general "problem".
The issue is that when I kill my app (and this is important to reproduce the issue) and I get closer to my beacon, iOS fire an event regionDidEnter that is caught by a file wrote in native code, and then, sent to javascript using the method of RCTEventEmitter: [self sendEventWithName:#"regionDidEnter" body:event];
The problem is that this event is fired before javascript is fully loaded, so my listener:
// component.js
Beacons.BeaconsEventEmitter.addListener('regionDidEnter', b => {
//code
});
doesn't get called.
Order of events:
[BeaconsDemo] Did finish launching enter
[BeaconsDemo] Did finish launching After jsBundleURLForBundleRoot
[BeaconsDemo] Did finish launching After RCTRootView alloc
[BeaconsDemo] Did finish launching After UIWindow alloc
[BeaconsDemo] Did finish launching After makeKeyAndVisible
[BeaconsDemo] Did finish launching end
--iOS send the event and it is caught by RNiBeacon but it has no listeners yet--
[BeaconsDemo] no listeners in RnIBeacon.m
--Register
[BeaconsDemo] regionDidExit
-- First line of javascript --
[BeaconsDemo] start observing
[BeaconsDemo] requestAlwaysAuth
Any idea to handle this situation? Is there any way or approach to send the event through RCTEventEmitter waiting for the javascript is loaded?
Thanks
This general problem also exists when writing native beacon apps for iOS or Android. The solution is that you must set up your "hook" that enables beacon monitoring and adds the event listener (or delegate / notification callback as it is called in native code) in the appropriate place per platform:
iOS: AppDelegate.didFinishLanching(options: )
Android: Application.onCreate()
The key is that this setup must be complete before those methods return.
This same rule applies for ReactNative. The trick with ReactNative, is that by default JavaScript does not run on these events -- it runs only when a screen is launched, which sets up the hook too late for the above to work when your app is killed. To get this to work, you'll need to eject your app so you can set up some custom native code in the above callbacks.
Two options:
Implement beacon detection natively (easiest, but requires native coding on a per-platform basis)
Add native code in the above native callbacks that starts a RCTBridge on iOS, and launches a view that executes code that triggers our JavaScript code to set up beacon detection. On Android, the equivalent would be to construct a new ReactRootView and ReactNativeInstanceManager in a way that it triggers your JavaScript code to set up beacon detection.
I have not personally tested option 2 to see if it can set up the hooks soon enough. If it works, it will certainly be harder than the native solution, but allows you to keep your beacon detection logic in JavaScript.
(https://github.com/MacKentoch/react-native-beacons-manager/issues/50)
I've forked #newoceaninfosys' fork and added the 'missed beacon' method. Check my last 3 commits for how to replicate it.
(https://github.com/iamandiradu/react-native-beacons-manager)
Use it by adding this in your didMount function:
if (Platform.OS === 'ios') {
// iOS cold start listener
this.beaconMissedListener = Beacons.BeaconsEventEmitter.addListener(
'onMissedBeacon',
data => {
if (data) {
this._beaconListener(data);
}
},
);
Beacons.getMissedBeacon();
}
This will retrieve the data that got 'lost' because the event triggered faster than the listener.
(React Native (iOS): RCTEventEmitter before javascript is loaded)
Hope this helps someone. :)
I want to change userData path with path defined by user. So, I'm fetching the path from UI, storing it into a file. So that next time app launches, it changes the path.
I wanted to restart the app as soon as user has selected the path. I tried app.relaunch() function. But it didn't work, neither it returned error.
I used exact same example mentioned in documentation. http://electron.atom.io/docs/api/app/#apprelaunchoptions
Calling app.relaunch() will not actually quit the app, you need to follow it by a call to app.quit() or app.exit().
app.relaunch();
app.quit();
This code must work, but Please Note that while debugging (i.e. in Visual studio code) after app.quit() debugger disconnects and kills whole application, therefore app will not restart. You might want to test it on application that is already installed or run through npm.
I've got desktop application that works as simple browser. It has tabs with webviews that show web pages.
Some websites have social sign-in option. And I really need it to be available for my users.
In general browser (e.g. chrome) when you sign in via Facebook, it creates new window with social authorization form. After submitting this form the new window closes and parent window receives callback code, reloads and sign you in.
In my app when you try to do the same, webview that contains the web page, creates new BrowserWindow with the same auth form. At this point everything works fine. When you submit form, the new window closes but nothing happens after. Parent window doesn't receive any callback, the code that should run isn't triggered at all. Or probably it has been received, but not triggered, since if I reload page, it shows that I'm signed in.
According to the electron documentation <webview> runs in a separate process than renderer process. So I think when new window closes, the callback is received by parent BrowserWindow (renderer process) and not runs exactly in webview frame.
While searching through different sources for solution, I found that this problem is also concerned with window.opener, that was not fully supported in the earliest versions of electron. But according to the issue#1865 on github full window.opener support is now available if I open window by window.open method.
To make this work I should prevent webview to open new BrowserWindow and create it by myself.
The following code fails:
// in renderer process
webview.addEventListener('new-window', function(event) {
event.preventDefault();
// I also tried 'event.defaultPrevented = true;'
// or 'return false;'
});
The listener is triggered, but I can't prevent new-window opening.
Someone wrote that new-window can be prevented in main process only.
So I add the following code in my main.js:
// in main process
var electron = require('electron');
var BrowserWindow = electron.BrowserWindow;
mainWindow = new BrowserWindow({
"width": 970,
"height": 500,
"center": true,
'title': 'Main window',
"autoHideMenuBar": true
});
mainWindow.loadURL('file://' + root + '/index.html');
mainWindow.webContents.on('new-window', function(event) {
console.log(event);
});
This time event 'new-window' is not triggered at all. I think this is because the event works only if renderer process requests to open a new window by window.open for example. But in my case new window is opened by <webview> tag, not by renderer process. So I failed to prevent new-window to open since these are two separate processes.
So I decided just to get new-window after it opens.
// in renderer process
var electron = require("electron");
var remote = electron.remote;
webview.addEventListener('new-window', function(event) {
var win = remote.BrowserWindow.getFocusedWindow();
var contents = win.webContents;
win.setIcon(root+"/img/favicon.ico");
win.setMenuBarVisibility(false);
contents.on('dom-ready', function() {
contents.openDevTools();
})
});
After that I really have no idea what to do next... how to append window.opener and solve my issue
I found many similar issues on github. But all of them are merged to one issue #1865 and there is no clear answer so far.
I know solution exists. Brave developers fixed this. Brave browser also made with electron works correctly the same as general browser does. But I can't find answer while looking through its sources.
They even made tests to check whether window.opener works correctly. But I failed to install Brave from git repository. After 'npm-start' it says 'Error: %1 is not valid Win32 application'. I have Windows 10. (just another reason makes me switch to Linux. Hope to make it soon.)
So, my question is: how to send callback data from new window back to webview that created this window. Thanks in advance.
First, to handles new-window popups yourself, you have to disable allowpopups (i.e. remove allowpopups attribute from the webview)
This way the new-window event will be triggered but no popup will be opened until you explicitelly code it.
For the window.opener feature which is required for some social or sso login, electron does not have enough support of it. See issue 1865
The Brave team have implemented it in a restricted way in their fork of Electron. See issue 4175.
You may try to either:
Use the fork of electron from Brave (not sure it will fit your needs as they have made some major changes)
Make a PR to electron to integrate the support of window.opener from the fork.
Make your own polyfill for the methods you need by injecting a script that implements missing window.opener methods. You can use window.opener.send to communicate with renderer process)