I'm building a system resource monitor as a project using electron and aurelia.
Main.js
var ramInfo = {};
var result = await si.mem()
ramInfo.total = parseInt(result.total / 1024 / 1024);
ramInfo.used = parseInt(result.used / 1024 / 1024);
ramInfo.percentUsed = parseInt((ramInfo.used / ramInfo.total) * 100);
ramInfo.percentAvailable = parseInt((ramInfo.percentUsed - 100) * -1);
event.sender.send('ram-reply', ramInfo);
})
Overview.js:
async attached () {
await this.getRamInfo();
this.startDataRefresh();
}
async getRamInfo () {
window.ipc.send('ram');
await window.ipc.on('ram-reply', (event, result) => {
this.system.ram = result;
//This line gets logged an additional time each time the setInterval function runs
console.log(this.system.ram);
this.ramData.series = [this.system.ram.percentAvailable, this.system.ram.percentUsed];
new Chartist.Pie('.ram-chart', this.ramData , this.options);
});
console.log("Break");
}
startDataRefresh() {
let scope = this;
setInterval(function() {
scope.getRamInfo();
}, 3000);
}
I am receiving the folowing error in my electron console:
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 ram-reply listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
I would only think that the getRamInfo() function would run once every three seconds, however, the console.log portion of the function is getting logged an additional time each time the function runs. I'm fairly certain this is where the issue lies, I'm just not sure why it is running multiple times per interval.
EDIT:
I've reached a partial solution in moving the setInterval function into main.js:
ipcMain.on('ram', async (event) => {
setInterval(async function() {
var ramInfo = {};
var result = await si.mem()
ramInfo.total = parseInt(result.total / 1024 / 1024);
ramInfo.used = parseInt(result.used / 1024 / 1024);
ramInfo.percentUsed = parseInt((ramInfo.used / ramInfo.total) * 100);
ramInfo.percentAvailable = parseInt((ramInfo.percentUsed - 100) * -1);
event.sender.send('ram-reply', ramInfo)
}, 3000);
})
It seems like each time the original setInterval called to ipcMain this created a new listener and each time every listener returned the results. I would like it to be dependant on the view that is open so controlling this via the view would be preferable.
Try this:
async getRamInfo () {
window.ipc.send('ram');
return new Promise(resolve => window.ipc.once('ram-reply', (event, result) => resolve(result));
}
async refresh() {
const ramInfo = await this.getRamInfo();
this.ramData.series = [this.system.ram.percentAvailable, this.system.ram.percentUsed];
new Chartist.Pie('.ram-chart', this.ramData , this.options);
// ...
}
startDataRefresh() {
if(!this.interval) {
this.interval = setInterval(() => this.refresh(), 3000);
}
}
stopDataRefresh() {
if(this.interval) {
clearInterval(this.interval);
delete this.interval;
}
}
main part - window.ipc.once('ram-reply' - use once for one-time event subscription
Related
This works perfectly in Android.
public async Task<double> UploadData()
{
double steps = 0.0;
await _healthData.GetSteps((totalSteps) =>
{
SentrySdk.CaptureMessage("totalSteps = " + totalSteps);
MainThread.BeginInvokeOnMainThread(() =>
{
steps = totalSteps;
//Task.Delay(1000);
});
});
SentrySdk.CaptureMessage("UploadData steps = " + steps);
var fitness = new Fitness();
fitness.Steps = steps;
await _restService.SaveItemAsync(fitness, true);
return steps;
}
In iOS, totalSteps is correct, but steps is still 0 when fitness.Steps = steps runs. Bottom line, I can't get the totalSteps value from inside the _healthData.GetSteps((totalSteps) operation. The Android Google Fit and iOS HealthKit API calls run with completion handlers.
At this stage, I'm just trying to figure out how to upload data (steps, calories, active minutes, distance) to my server.
Does anyone know how to make this work? I can display all the data (steps, calories, active minutes, distance) in a View using an ObservableCollection.
I got this to work by embedding the calls like so.
_healthData.GetHealthPermissionAsync((result) =>
{
if (result)
{
_healthData.FetchSteps((totalSteps) =>
{
_healthData.FetchMetersWalked((metersWalked) =>
{
_healthData.FetchActiveMinutes((activeMinutes) =>
{
_healthData.FetchActiveEnergyBurned((caloriesBurned) =>
{
var fitness = new Fitness();
fitness.Steps = totalSteps;
fitness.Calories = caloriesBurned;
fitness.Distance = metersWalked;
fitness.Minutes = activeMinutes;
_restService.SaveItemAsync(fitness, true);
});
});
});
});
}
});
We are trying web crawl and get contents from multiple pages. I am taking the advantage of async API with Promise ALL which can execute requests in parallel.
Is there a limitation on the number of contexts which can be opened parallel?
const fs = require('fs');
let browser;
const batch_size = 4; // control the number of async parallel calls
(async () => { // main function
let urls = [];
urls = fs.readFileSync('./resources/input_selenium_urls.csv').toString().split("\n");
browser = await chromium.launch();
let context_size = 0;
let processUrls = [];
let total_length = 0;
for (let i=0;i<urls.length;i++,total_length++) {
if ((context_size==batch_size)||(i==urls.length-1)){
await Promise.all(processUrls.map(x => getHTMLPageSource(x)));
context_size = 0;
processUrls = [];
} else {
processUrls.push(urls[i]);
context_size++;
}
}
await browser.close();
})();
async function getHTMLPageSource(url) {
const context = await browser.newContext();
const page = await context.newPage();
let response = {}
try {
await page.goto(url, { waitUntil: 'networkidle' });
response = {
url : url,
content: await page.title(),
error : null
}
console.log(response);
}
catch {
response = {
error : "Timeout error"
}
}
context.close;
return response;
}
Browser contexts are cheap to create, but it's not clear whether there is a hard-coded limit on them from the docs perhaps the limit might depend on the browser you chose and your OS resources. I think you might only be able to find out by creating a lot of contexts.
Wanted to check how many instances are running and control the number of instances running in one exe electron bundle. Let us say I wanted to allow only three instances running for the one exe bundle. I am not able to do this.
Current Behavior:
Only one and remaining can block. Or open for any number of instances. We need to control only three instances running, not more than that.
Example:
const { app } = require('electron')
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
}
})
// Create myWindow, load the rest of the app, etc...
app.on('ready', () => {
})
}
You can try with the following code to know how many windows have been opened.
const count = BrowserWindow.getAllWindows().length;
To check visible windows, you can try the following code
let count = BrowserWindow.getAllWindows()
.filter(b => {
return b.isVisible()
}).length
Once you get the number of instances, based upon the condition for number of instance, ie. if it is more than 3, you can quit using app.quit().
You can make each instance write to a file (increment a counter for example) when the instance starts and when it exits. (decrement the counter). You should check that file to see if the maximum number of instances are running
import { app } from "electron";
import path from "path";
import fs from "fs";
const MAX_APP_INSTANCES = 3;
const INSTANCE_COUNT_FILE_PATH = path.join(
app.getPath("userData"),
"numOfInstances"
);
// utils to read/write number of instances to a file
const instanceCountFileExists = () => fs.existsSync(INSTANCE_COUNT_FILE_PATH);
const readInstanceCountFile = () =>
parseInt(fs.readFileSync(INSTANCE_COUNT_FILE_PATH, "utf-8"));
const writeInstanceCountFile = (value) =>
fs.writeFileSync(INSTANCE_COUNT_FILE_PATH, value);
const incInstanceCountFile = () => {
const value = readInstanceCountFile() + 1;
writeInstanceCountFile(value.toString());
};
const decInstanceCountFile = () => {
const value = readInstanceCountFile() - 1;
writeInstanceCountFile(value.toString());
};
// logic needed to only allow a certain number of instances to be active
if (instanceCountFileExists() && readInstanceCountFile() >= MAX_APP_INSTANCES) {
app.quit();
} else {
if (!instanceCountFileExists()) {
writeInstanceCountFile("1");
} else {
incInstanceCountFile();
}
app.on("quit", () => decInstanceCountFile());
}
Note: this is solution is somewhat hacky. For example, the quit event is not guaranteed to fire when the Electron app exits
I'm using Xamarin.Forms and I have implemented ZXing.Net.Mobile for scanning bar codes.
On Android it's working fine, on iOS 10 after reading a barcode the function "OnScanResult" is fired and executes the command Navigation.PopAsync() which closes the scanning page but after a second it closes also the current page where I have displayed the result !
MyTapScan.Tapped += async (sender, e) =>
{
await MyBtScan.ScaleTo(1.20, 100, Easing.Linear);
await MyBtScan.ScaleTo(1, 100, Easing.Linear);
await Task.Delay(50);
//--------------------------------------------
MyAppLib.MyAppUtilitiesBarCodeReader MyBarCodeReader = new MyAppLib.MyAppUtilitiesBarCodeReader();
var MyScannerPage = MyBarCodeReader.GetBarCodeReaderPage();
//--------------------------------------------
MyScannerPage.OnScanResult += (result) => {
//Stop scanning
MyScannerPage.IsScanning = false;
//Pop the page and show the result
Device.BeginInvokeOnMainThread(() => {
Navigation.PopAsync();
MyMachSerialNumber.Text = result.Text;
});
};
//--------------------------------------------
//Display scanner
await Navigation.PushAsync(MyScannerPage);
};
Please please help..!! :)
Every time MyTapScan.Tapped is called you are subscribing to MyScannerPage.OnScanResult so if you tap button 5 times your OnScanResult will be called 5 times. I hope now you know how to solve that.
One of possible solutions:
Take your OnScanResult delegate and make it separate function, let say ScanFinished. Then instead of
MyScannerPage.OnScanResult += (result)
do
MyScannerPage.OnScanResult -= ScanFinished;
MyScannerPage.OnScanResult += ScanFinished;
Then you can be sure the event unsubscribed before you subscribe to it again
I have introduced a new variable to check if the scanning was already fired and now it's working fine and as expected.
This is the code:
MyTapScan.Tapped += async (sender, e) =>
{
await MyBtScan.ScaleTo(1.20, 100, Easing.Linear);
await MyBtScan.ScaleTo(1, 100, Easing.Linear);
await Task.Delay(50);
bool MyIsScanning = true;
//--------------------------------------------
MyAppLib.MyAppUtilitiesBarCodeReader MyBarCodeReader = new MyAppLib.MyAppUtilitiesBarCodeReader();
var MyScannerPage = MyBarCodeReader.GetBarCodeReaderPage();
//--------------------------------------------
MyScannerPage.OnScanResult += (result) => {
//Stop scanning
MyScannerPage.IsScanning = false;
//Pop the page and show the result
Device.BeginInvokeOnMainThread(() => {
if (MyIsScanning == true)
{
MyIsScanning = false;
MyMachSerialNumber.Text = result.Text;
Navigation.PopAsync();
}
});
};
//--------------------------------------------
//Display scanner
await Navigation.PushAsync(MyScannerPage);
};
I'm tracking events in a web page using RxJs and sending them through buffer.
I emit a buffer on unload event, but it's done after the event is triggered, so it's never triggered.
Is there a way to generate everything related to my buffer on unload ?
/* globals $ */
import { Observable } from 'rx-dom'
import { Load, Unload } from './events'
$(function () {
const load$ = (new Load()).observer$() // triggered on page load
const unload$ = (new Unload()).observer$() // triggered on page unload
const source$ = Observable.merge(load$, unload$)
const intervalBetween = 5000
const dummyStart$ = Observable.return({})
const bufferizeEvents$ = [submit$, unload$]
const opening$ = dummyStart$
.concat.apply(dummyStart$, bufferizeEvents$)
.flatMapLatest(x => Observable.timer(0, intervalBetween))
.skip(1)
const buffers$ = source$.buffer(opening$)
buffers$.subscribe(buffer => {
if (buffer.length) {
// some ajax call
// this is never reached on page unload
}
})
})