improve advice on callbacks passed from renderer to main

Remote is a great feature, it's a shame to put people off unnecessarily. I think the original warnings given are too extreme

The potential bugs that stem from not cleaning up event handlers (or any reference) are present in any Javascript code. We don't avoid using event-handlers in the DOM because we might forget to clean them up!

I've added an example of the behaviour of return values from synchronously called callbacks from renderer, and have changed the advice from 'you shouldn't do this' to 'be careful when you do this'.
This commit is contained in:
Tim Ruffles 2015-08-27 17:10:02 +01:00
parent 195be931a4
commit 4bc9bf7654

View file

@ -52,16 +52,43 @@ Primary value types like strings and numbers, however, are sent by copy.
## Passing callbacks to the main process ## Passing callbacks to the main process
Some APIs in the main process accept callbacks, and it would be tempting to Code in the main process can accept callbacks from the renderer - for instance the `remote` module -
pass callbacks when calling a remote function. The `remote` module does support but you should be extremely careful when using this feature.
doing this, but you should also be extremely careful with this.
First, in order to avoid deadlocks, the callbacks passed to the main process First, in order to avoid deadlocks, the callbacks passed to the main process
are called asynchronously, so you should not expect the main process to are called asynchronously. You should not expect the main process to
get the return value of the passed callbacks. get the return value of the passed callbacks.
Second, the callbacks passed to the main process will not get released For instance you can't use a function from the renderer process in a `Array.map` called in the main process:
automatically after they are called. Instead, they will persistent until the
```javascript
// main process mapNumbers.js
exports.withRendererCallback = function(mapper) {
return [1,2,3].map(mapper);
}
exports.withLocalCallback = function() {
return exports.mapNumbers(function(x) {
return x + 1;
});
}
// renderer process
var mapNumbers = require("remote").require("mapNumbers");
var withRendererCb = mapNumbers.withRendererCallback(function(x) {
return x + 1;
})
var withLocalCb = mapNumbers.withLocalCallback()
console.log(withRendererCb, withLocalCb) // [true, true, true], [2, 3, 4]
```
As you can see, the renderer callback's synchronous return value was not as expected,
and didn't match the return value of an indentical callback that lives in the main process.
Second, the callbacks passed to the main process will persist until the
main process garbage-collects them. main process garbage-collects them.
For example, the following code seems innocent at first glance. It installs a For example, the following code seems innocent at first glance. It installs a
@ -74,14 +101,16 @@ remote.getCurrentWindow().on('close', function() {
}); });
``` ```
The problem is that the callback would be stored in the main process until you But remember the callback is referenced by the main process until you
explicitly uninstall it! So each time you reload your window, the callback would explicitly uninstall it! If you do not, each time you reload your window the callback will
be installed again and previous callbacks would just leak. To make things be installed again, leaking one callback each restart.
worse, since the context of previously installed callbacks have been released,
when the `close` event was emitted, exceptions would be raised in the main process.
Generally, unless you are clear what you are doing, you should always avoid To make things worse, since the context of previously installed callbacks have been released,
passing callbacks to the main process. when the `close` event was emitted exceptions would be raised in the main process.
To avoid this problem, ensure you clean up any references to renderer callbacks passed to the main
process. This involves cleaning up event handlers, or ensuring the main process is explicitly told to deference
callbacks that came from a renderer process that is exiting.
## remote.require(module) ## remote.require(module)