Giter VIP home page Giter VIP logo

electron-remote's Introduction

DEPRECATED: electron-remote: an asynchronous 'remote', and more

No Maintenance Intended

This project is no longer maintained, pull requests are no longer being reviewed or merged and issues are no longer being responded to.


electron-remote provides an alternative to Electron's remote module based around Promises instead of synchronous execution. It also provides an automatic way to use BrowserWindows as "background processes" that auto-scales based on usage, similar to Grand Central Dispatch or the .NET TPL Taskpool.

The Quickest of Quick Starts

Calling main process modules from a renderer
import { createProxyForMainProcessModule } from 'electron-remote';

// app is now a proxy for the app module in the main process
const app = createProxyForMainProcessModule('app');

// The difference is all methods return a Promise instead of blocking
const memoryInfo = await app.getAppMemoryInfo();
Calling code in other windows
import { createProxyForRemote } from 'electron-remote';

// myWindowJs is now a proxy object for myWindow's `window` global object
const myWindowJs = createProxyForRemote(myWindow);

// Functions suffixed with _get will read a value
userAgent = await myWindowJs.navigator.userAgent_get()
Renderer Taskpool
import { requireTaskPool } from 'electron-remote';

const myCoolModule = requireTaskPool(require.resolve('./my-cool-module'));

// This method will run synchronously, but in a background BrowserWindow process
// so that your app will not block
let result = await myCoolModule.calculateDigitsOfPi(100000);

But I like Remote!

Remote is super convenient! But it also has some downsides - its main downside is that its action is synchronous. This means that both the main and window processes will wait for a method to finish running. Even for quick methods, calling it too often can introduce scroll jank and generally cause performance problems.

electron-remote is a version of remote that, while less ergonomic, guarantees that it won't block the calling thread.

Using createProxyForRemote

createProxyForRemote is a replacement for places where you would use Electron's executeJavaScript method on BrowserWindow or WebView instances - however, it works a little differently. Using a new feature in ES2015 called proxy objects, we create an object which represents the window object on a remote context, and all method calls get sent as messages to that remote instead of being run immediately, which feels very similar to the remote Electron module.

This provides a number of very important advantages:

  • createProxyForRemote uses asynchronous IPC instead of blocking
  • Parameters are serialized directly, so you don't have to try to build strings that can be evald, which is a dangerous endeavor at best.
  • Calling methods on objects is far more convenient than trying to poke at things via a remote eval.

How do I get properties if everything is a Promise tho???

Astute observers will note, that getting the value of a property is always a synchronous operation - to facilitate that, any method with _get() appended to it will let you fetch the value for the property.

import { createProxyForRemote } from 'electron-remote';

// myWindowJs is now a proxy object for myWindow's `window` global object
const myWindowJs = createProxyForRemote(myWindow);

// Functions suffixed with _get will read a value
myWindowJs.navigator.userAgent_get()
  .then((agent) => console.log(`The user agent is ${agent}`));

But do this first!

Before you use createProxyForRemote, you must call initializeEvalHandler() in the target window on startup. This sets up the listeners that electron-remote will use.

Bringing it all together

// In my window's main.js
initializeEvalHandler();
window.addNumbers = (a,b) => a + b;


// In my main process
let myWindowProxy = createProxyForRemote(myWindow);
myWindowProxy.addNumbers(5, 5)
  .then((x) => console.log(x));

>>> 10

Using createProxyForMainProcessModule

This is meant to be a drop-in replacement for places you would have used remote in a renderer process. It's almost identical to createProxyForRemote, but instead of evaling JavaScript it can only call methods on main process modules. It still has all the same benefits: asynchronous IPC instead of an ipc.sendSync.

Here Be Dragons

electron-remote has a number of significant caveats versus the remote module that you should definitely be aware of:

  • Remote values must be Serializable

Objects that you return to the calling process must be serializable (i.e. you can call JSON.stringify on it and get a valid thing)- this means that creating Classes won't work, nor will return objects like BrowserWindows or other Electron objects. For example:

let myWindowProxy = createProxyForRemote(myWindow);

// XXX: BAD - HTML elements aren't serializable
let obj = myWindowProxy.document.createElement('h1');
  • Remote event listeners aren't supported

Anything that involves an event handler isn't going to work:

// XXX: BAD - You can't add event handlers
myWindowProxy.document.addEventListener('onBlur', (e) => console.log("Blur!"));

The Renderer Taskpool

Renderer Taskpools provide an automatic way to use BrowserWindows as "background processes" that auto-scales based on usage, similar to Grand Central Dispatch or the .NET TPL Taskpool. This works by allowing you to provide a Module that you'd like to load in the remote processes, which will be loaded and unloaded on the fly according to demand.

Let's look at the example again:

import { requireTaskPool } from 'electron-remote';

const myCoolModule = requireTaskPool(require.resolve('./my-cool-module'));

// This method will run synchronously, but in a background BrowserWindow process
// so that your app will not block
let result = await myCoolModule.calculateDigitsOfPi(100000);

By default, requireTaskPool will create up to four background processes to concurrently run JS code on. As these processes become busy, requests will be queued to different processes and wait in line implicitly.

More Dragons

Since requireTaskPool will create and destroy processes as needed, this means that global variables or other state will be destroyed as well. You can't rely on setting a global variable and having it persist for a period of time longer than one method call.

The remote-ajax module

One module that is super useful to have from the main process is a way to make network requests using Chromium's networking stack, which correctly does things such as respecting the system proxy settings. To this end, electron-remote comes with a convenient wrapper around Rx-DOM's AJAX methods called remote-ajax.

import { requireTaskPool } from 'electron-remote';

const remoteAjax = requireTaskPool(require.resolve('electron-remote/remote-ajax'));

// Result is the object that XmlHttpRequest gives you
let result = await remoteAjax.get('https://httpbin.org/get');
console.log(result.url)

>>> 'https://httpbin.org/get'

See the documentation for Rx-DOM for how these methods work.

Another method that is included is downloadFileOrUrl, which lets you download a file to a target:

/**
 * Downloads a path as either a file path or a HTTP URL to a specific place
 *
 * @param  {string} pathOrUrl   Either an HTTP URL or a file path.
 * @return {string}             The contents as a UTF-8 decoded string.
 */
function downloadFileOrUrl(pathOrUrl, target)

electron-remote's People

Contributors

anaisbetts avatar ccnokes avatar charliehess avatar felixrieseberg avatar malept avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

electron-remote's Issues

This module does not pass nodeIntegration:true so you can't `require` in pooled tasks anymore.

This is mostly a note to save folks who are debugging problems with this module and newer versions of Electron. This line:

let bw = new BrowserWindow({width: 500, height: 500, show: false});

Needs to be updated to enable nodeIntegration, or calling require in your task pool root script won't work. This is breaking electron-spellcheck and probably others, but they should probably move away from this module if possible since it's been deprecated!

Related: electron-userland/electron-spellchecker#152

Sending array back to main renderer

Hello there,

I'm trying to figure out more about electron-remote and maybe I'm misunderstanding the documentation.

I'm trying to use electron-remote to execute database jobs.

In my main renderer I have the follow:

var electron = require('electron');
var Datastore = require('nedb');
var db_schedule = new Datastore({ filename: 'data/schedule', autoload: true });

const { requireTaskPool } = require('electron-remote');
const homePath = (electron.app || electron.remote.app).getPath('home');
const Database = requireTaskPool(require.resolve(homePath + '/app/src/renderer/workers/database.js'));

$(document).ready(function () {

Database.insert_test( 'dashboard' ).then(result => {
        console.log('work done');
        console.log(result);
    });

Database.find_all( 'dashboard' ).then(result => {
        console.log('work done');
        console.log(result);
    });

});

And then I have my worker in database.js as follow:

var Datastore = require('nedb');
var electron = require('electron');
const homePath = (electron.app || electron.remote.app).getPath('home');

function find_all( database ) {
    let moment = require('moment');

    let db = new Datastore({ filename: homePath + '/app/data/' + database, autoload: true });

    let missed_events = [];
    let priority_events = [];

    const query = db.find( { type: "event" } , function (err, queryObject) {

        queryObject.forEach( function (value) {
            if ( moment(value.start).isBefore(moment(), 'day') ) {
                missed_events.push(value);
            }
            else if ( value.status || moment(value.start).isSame(moment(), 'day') ) {
                priority_events.push(value);
            }
        });
    });
    
    return {
        missed_events: missed_events,
        priority_events: priority_events,
    };
}

function insert_test( database ) {
    var db = new Datastore({ filename: homePath + '/app/data/' + database, autoload: true });

    let query_string = { type: "test", value: true }

    const query = db.insert( query_string, function (err) {});

    return {
        msg: "inserted",
    }
}

module.exports = {
    find_all: find_all,
    insert_test: insert_test
}

Those arrays missed_events and priority_events both returns empty, but it's not a database problem since the db.insert from the insert_test function works and successfully inserts the data.

Looks like electron-remote can't handle returning arrays right?
What do you suggest to use then, or what can I do to solve this limitation?

Since I want to use electron-remote to attenuate the bottleneck of too much nedb CRUD that I needed.

Returning a Promise from 'executeMainProcessMethod'

In execute-js-func.js initializeEvalHandler uses a function executeMainProcessMethod which is not prepared to handle Promises. This causes issues when paired with createProxyForMainProcessModule and working with Promises. There is another method, evalRemoteMethod, which is prepared to handle Promises. Could both cases (calling a renderer and calling the main process) be made to work with Promises?

The Renderer Taskpool

First off, thx for making this ๐Ÿ˜„

In the "The Renderer Taskpool" you use a hidden browser window

Renderer Taskpools provide an automatic way to use BrowserWindows as "background processes" that auto-scales based on usage

Do you have any options for setting the window to show with developer console, so its easy to debug during development?

Error: Cannot find module "." (in production build)

Hello,

I'm having an issue trying to make this module work in production mode.

Let's take by example this simple configuration:

FileUploader = ElectronRemote.requireTaskPool(require.resolve('../workers/uploadWorker'));
...
export class UploadManager {
    ...
    private async doUpload(file) {
        await FileUploader.doUpload(file);
    }
}

with uploadWorker.js being:

async function doUpload(file) {
    console.warn('uploading ' + file.name);
    // async code here
}

module.exports = { doUpload };

This configuration works like a charm when trying it in dev mode; while it seems to be failing in production build.

In particular, I get this exception:

Error: Cannot find module "."
    at webpackMissingModule (/Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:3879:84)
    at /Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:3879:163
    at Generator.next (<anonymous>)
    at step (/Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:3991:191)
    at /Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:3991:437
    at /Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:3991:99
    at rendererRequireDirect (/Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:3926:17)
    at getOrCreateWindow (/Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:4019:49)
    at DeferSubscriber._Observable.Observable.defer [as factory] (/Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:4031:16)
    at DeferSubscriber._callFactory (/Users/giovanni/Documents/my-electron-project/release/mac/Secret app name.app/Contents/Resources/app.asar/dist/main.js:24407:27)

At first I thought it was something caused by the async function, but even using a built in helper from this same library (FileUploader = ElectronRemote.requireTaskPool(require.resolve('electron-remote/remote-ajax'));) I get the same error.

A few already explored caveats:

  • asar packaging is turned on; turning it off does not help
  • The exception is thrown in the moment the code is called (FileUploader.doUpload(file);), not when the module is imported
  • The built main.js seems to correctly reference the module containing the remote code FileUploader = ElectronRemote.requireTaskPool(/*require.resolve*/(110));, where module 110 is defined and well-shaped:
...
function doUpload(file) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            console.warn('uploading ' + file.name);
            return [2 /*return*/];
        });
    });
}
module.exports = { doUpload: doUpload };

Any pointer on other things I should check?

Rookie alert: can't get electron-remote to work.

Hey,

I am probably missing something really simple here but I got 2 files. Renderer.js and yarn-global.js file.

So I import it like this

const { requireTaskPool } = require('electron-remote');
const yarnGlobal = requireTaskPool(require.resolve('./tasks/yarn-global'));

ipcRenderer.send('get-yarn-packages');

ipcRenderer.on('load-yarn-packages', event => {
  yarnGlobal.getGlobalYarnPackages('yarn global ls').then(result => {
    console.log(result);
  });
});

And in my yarn-global

const { exec } = require('child_process');

let getGlobalYarnPackages = command => {
  exec(command, (error, stdout, stderr) => {
    if (error) {
      console.error(`exec error: ${error}`);
      return;
    };

    return stdout.match(/"(.*?)"/g);;
  });
};

module.exports = {
  getGlobalYarnPackages
};

But what I get in console is undefined. What am I not getting right?

Babel 7 Compatibility

Trying to use Electron-Spellchecker which depends on this results in a failure due to the es2015-node5 babel plugin in the .babelrc

You can solve this by replacing the existing .babelrc for Electron-Remote with the following:

{
  "plugins": ["@babel/plugin-transform-async-to-generator"]
}

@paulcbetts what would be the best (most susttainable and user friendly) approach to supporting Babel 7?

Set timeout for task pool

Hi there,

I'm trying to get a long running CPU intensive task to run as a background job with requireTaskPool , however get a timeout error since the method I'm calling takes a while to complete.

Is there a way to set (or remove) the timeout explicitly?

Thanks

Events from remote task

I apologize if this is a naive question. I'm using requireTaskPool to execute a background job in my app, however would like that job to report back on its progress.

Is there a way to to send events from that background task to the renderer process that called it?

Thanks!

Module not found

I added

import { requireTaskPool } from 'electron-remote'

to my component.

This causes the following error:

ERROR in ./app/components/Navbar.js
Module not found: Error: Cannot resolve module 'electron-remote' in C:\Users\alex\kapla\app\components
 @ ./app/components/Navbar.js 29:22-48
webpack: bundle is now VALID.

electron-remote is installed in ./node_modules.

link to alternate solution?

Hi, I just published a library that uses an approach different from electron-remote to tackle the main headache of working with remote processes. It has a pro/con profile that's also quite different so would you be open to linking to it in the electron-remote README, for people to consider as an alternative?
@wranggle/rpc

It's mostly syntactic sugar, using pure message passing but removing all of the associated event-listening add/remove clutter that causes most of the headaches. It doesn't actually pass code or use eval, which is nice in some ways but also limiting in some, like the chained method calls that electron-remote supports. WranggleRpc will work in any process or window though and over WebSockets and postMessage (for iframes, service workers, etc.)

Why deprecated?

Has it been deprecated in favour of some other package, or idiom, that we should all be using? (If so, which one?)

Or is it just because it is marked as "No Maintenance Intended" ? (In which case, and if there is no superior alternative to switch to, advertising for a new maintainer might be clearer?)

Uncaught TypeError: request.charCodeAt is not a function

I'm not sure if this is a webpack issue, but I'm doing the following:

const fetchProjects = requireTaskPool(require.resolve('./Projects/fetchProjects'))

fetchProjects(this.existingProjects)

./Projects/fetchProjects.js

function fetchProjectsWorker (existingProjects) {

}

module.exports = fetchProjectsWorker

But I get Uncaught TypeError: request.charCodeAt is not a function

Debugging with console.log(require.resolve('./Projects/fetchProjects')) returns 100, so I think that might be the issue?

Issue after calling the remote method

Hi again.

I think both the "require" works after the version 1.1.1, but now I am having the below issue:

execute-js-func.js:253 Uncaught (in promise) Error: Couldn't access part of the object window.requiredModule.sumUp

the code to makes the call is pretty simple


a js file under renderer

const { requireTaskPool } = global.require('electron-remote')
const remote = require(global.require.resolve('./remote')

remote.sumUp(paramA, paramB).then((result) => { console.log(result) })


remote.js

function sumUp(paramA, paramB) {
    return paramA + paramB
}

module.exports = sumUp

Any idea? Thanks very much.

Missing RxJS operator?

I'm getting this error in 1.0.6:

If I do this, the first 4 calls don't throw errors but then after that it throws and doesn't complete 25 instances of work().

_.times(25, () => {
    work().then(() => {
      console.log(`work done`);
    });
  });

Here's the error:

Subscriber.ts:241 Uncaught TypeError: _Observable.Observable.timer is not a function
	@	custom-operators.js:15

Adding this line require('rxjs/add/observable/timer'); to custom-operators.js seems to fix it.

Promises resolving to an error

I am trying to handle errors generated when calling someRendererJS.remoteFunctionCall() with the standard promise error handler ( someRendererJS.remoteFunctionCall().then().then()......error() ), but any time I stick .error(function(e){}) into the promise flow I get told by Electron that "that function (the entire promise tree in that call) is not a valid function". Is the standard error handler not available, and if so, how can I handle promises that are rejected / result in errors?

Webpack build warning

Hi

I get the below warning from webpack when importing a module from electron-remote

WARNING in ./~/electron-remote/lib/renderer-require.js 23:19-46 Critical dependency: the request of a dependency is an expression

And when the app loads, I get an error from the remote module of electron
file:///C:/DEVEL/node_modules/electron/dist/resources/electron.asar/renderer/api/remote.js
Uncaught Error: path must be a string
AssertionError: path must be a string
at Module.require (module.js:497:3)

Any ideas?

_hashids2.default is not a constructor

Trying to import requireTaskPool using:

import { requireTaskPool } from 'electron-remote';
Error comes up:


TypeError: _hashids2.default is not a constructor
    at Object.<anonymous> (/Users/tuhin/id-desktop/node_modules/electron-remote/lib/execute-js-func.js:123:17)
    at Object.<anonymous> (/Users/tuhin/id-desktop/node_modules/electron-remote/lib/execute-js-func.js:685:3)
    at Module._compile (module.js:642:30)
    at Object.Module._extensions..js (module.js:653:10)
    at Module.load (module.js:561:32)
    at tryModuleLoad (module.js:504:12)
    at Function.Module._load (module.js:496:3)
    at Module.require (module.js:586:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/node_modules/electron-remote/lib/index.js:3:22)

fs.existsSync is not a function

First of all, thanks for this amazing idea.
I am dealing with an issue that when I add the following :
const { requireTaskPool } = require('electron-remote')

the debug console from electron would raise

Uncaught TypeError: fs.existsSync is not a function

I traced it and it looks like this

var fs = require('fs')
var path = require('path')

var pathFile = path.join(__dirname, 'path.txt')

if (fs.existsSync(pathFile)) {
  module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
} else {
  throw new Error('Electron failed to install correctly, please delete node_modules/' + path.basename(__dirname) + ' and try installing again')
}

I am using Node 6.5, electron 1.4.15 with browserify. Any idea?

Send IPC message from main process to a reader in the task pool

Hello,
I'm trying to send a message from the main process to all the render processes in the task pool.
Now I'm sending the message using the standard electron ipcMain.send() but unfortunately the message is not received by the background render processes.

Any idea on how to make it work?

Running requireTaskPool task indefinitely

First off, great codebase. Super useful and easy to use.

I am currently trying to start an express-based server through a requireTaskPool task and would like to leave it running "indefinitely". However, electron-remote automatically kills the server after idleTimeout (defaulting to 5000ms). Even if the server receives requests, idleTimeout does not refresh and electron-remote sees it as idle, killing the server.

It would be nice if I could pass in -1 or 0 to explicitly state that this process will run indefinitely until manually killed.

Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.