Im using is-port-reachable package that returns promise. I would like to find first free (not reachable) port. So I should perform IsPortReachable in loop. Im trying to use yield, but have an error SyntaxError: Unexpected strict mode reserved word (yield couldBeBinded). If I move yield outside promise an error will disappear.
findPort () {
function * checkPort() {
let couldBeBinded = false;
let port = this.port;
do {
IsPortReachable(this.port, {host: 'localhost'})
.then(function (reachable) {
"use strict";
if (!reachable) { // not binded
couldBeBinded = true;
} else {
port++;
}
if (port > 65534) {
return couldBeBinded;
}
yield couldBeBinded;
});
} while(!couldBeBinded && (port < 65534));
}
var generator = checkPort();
let couldBeBinded = { value: false, done: false };
while((couldBeBinded.value !== true) && (couldBeBinded.done !== false)) {
couldBeBinded = generator.next();
console.log(couldBeBinded);
}
}
May be my code is totally wrong, so please advice me how to loop promises on node. Thanks
Here's my solution to this problem:
const IsPortReachable = require('is-port-reachable');
var options = {
start_port : 2994,
end_port : 3003,
findFirstFree : true
}
function CheckPort(port, firstFree, callback) {
IsPortReachable(port, {host: 'localhost'})
.then((reachable) => {
if (reachable) {
console.log(port, "reachable");
if (firstFree) callback(port);
}
else {
console.log(port, "free")
if (!firstFree) callback(port);
}
})
}
function CheckPortHandler(port, firstFree) {
if (port >= options.end_port) return;
CheckPort(port + 1, firstFree, CheckPortHandler)
}
CheckPort(options.start_port, options.findFirstFree, CheckPortHandler);
You can change the start and end port in the options. If you want to find the first reachable port (as opposed to first free port) change findFirstFree to false.
In the code I'm creating a function CheckPort that wraps around IsPortReachable but also receives a callback. The callback is called if the reachable status does not agree with the firstFree parameter. The callback is another function that calls CheckPort again with an incremented port, so the process continues. It continues until the reachable status agrees with the firstFree param, so the callback is no longer called.
It might be a bit confusing but it works ok for me.
Here is the output with findFirstFree : true
2994 'free'
And the output with findFirstFree : false
2994 'free'
2995 'free'
2996 'free'
2997 'free'
2998 'free'
2999 'free'
3000 'reachable'
Related
I'm using discord.js v12, with the npm module linux-shell-command, run on ubuntu, and have added a feature that pings 3 web domains I manage to see if they're up.
var shellCommand = require("linux-shell-command").shellCommand;
var upDown = [];
if (args == []) {
// code not written yet
}
else {
try {
var domain=['example1.com','example2.com','example3.com'];
domain.forEach(site=>{
var sc=shellCommand(`ping -c 1 ${site} |grep '1 received, 0% packet loss'`);
sc.execute(upDown).then(success => {
if (success === true) {
var packet=sc.stdout;
packet=packet.slice(34,-10);
if (packet === " 0% packet loss") {
upDown.push(`The ${site} website is up!`);
}
}
else {
upDown.push(`The ${site} website is down!`);
}
}).catch(e => {
console.error(e);
});
});
}
catch (e) {
console.error(e);
}
finally {
console.log(upDown);
}
}
if I removed the forEach, I would have to essentially repeat the code block inside it for each domain, without the upDown array, so I tried it this way.
upDown.push() silently fails (nothing is added to the upDown array), no matter how many domains are present.
if I add upDown=upDown.join("\n"); to the finally block, before the console.log(), I get this error for each domain, pointing to the .push() that happens if the domain responds.
undefined
TypeError: upDown.push() is not a function
I'm totally confused, because if I use push right under the declaration of the upDown array, I can push no problem, and if I print upDown to the console just before that push, it sees the array, and it's contents. (verified by manually adding an item to the array declaration)
I ended up achieving this a somewhat different way. Here's the modified code, which does exactly what I wanted.
var shellCommand = require("linux-shell-command").shellCommand;
if (args == []) {
//code not written yet
}
else {
try {
var domain=['example1.com','example2.com','example3.com'];
domain.forEach(site =>{
var result;
var sc=shellCommand(`ping -c 1 ${site} |grep '1 received. 0% packet loss'`);
sc.execute().then(success => {
if (success === true) {
result="up";
}
else {
result="down";
}
console.log(`${site} is ${result}!`);
});
});
}
catch (e) {
console.error(e);
}
finally {
console.log("websites were checked!");
}
}
This puts the execution, based on the result, right inside the promise, eliminating the need for the extra array, without interrupting the flow.
Thanks for the help! :)
my root node file requires a module called q1 (not including all the required libraries as not relevant)
const analyzeSentiment = function(message) {
sentiment.getSentiment(message).then(result => {
return (result.vote === 'positive') ? handlePositive() : handleNegative();
});
}
const handlePositive = function() {
return `That's great, we have an opening next Friday at 3pm. Would that work for you?`;
}
const handleNegative = function() {
return `That's okay. Thanks for you time. If you change your mind, give us a call at (xxx) yyy-zzzz.`;
}
exports.analyzeSentiment = analyzeSentiment;
I call it like this: const message = require('q1').analyzeSentiment('text string');
With console logging I can see that it makes it down into the proper handlePositive or handleNegative methods, but nothing comes back. I've tried a few different ways but can't get it to work. Anyone have any suggestions, or see something blatantly wrong I'm doing? This is my first time working with node.
Your function analyzeSentiment not returning anything (see explanation further down).
Try this:
const analyzeSentiment = function(message) {
return sentiment.getSentiment(message).then(result => {
return (result.vote === 'positive') ? handlePositive() : handleNegative();
});
}
And in your caller:
require('q1').sentimentAnalyzer('text string').then(message => {
// Do your thing with the message here
});
Alternatively, if you are in an async context you can use await on the caller:
const message = await require('q1').sentimentAnalyzer('text string');
You might be wondering why the return (result.vote === ... isn't returning from your analyzeSentiment-function. The reason is that the you are creating an anonymous function with the arrow-expression result => ... in the then-block.
I am still trying to find a way to handle promises in a loop conditionally with the ability to break out of the loop.
Here is a short example
return new Promise(function (resolve, reject) {
if (ipAddresses.length > 0) {
let currentServer, agentOptions;
for (let i = 0; i < ipAddresses.length; i++) {
currentServer = ipAddresses[i];
agentOptions = {
};
// We need to block here
let isReachable = NetworkUtils.checkIfReachable(agentOptions, ip);
if (isReachable) {
resolve(currentServer);
// Break out of the loop
return currentServer;
}
else {
// Continue looping and trying to find a working server
}
}
reject(new Error("No working servers found"));
}
else {
resolve(new Error("No servers ips provided"));
}
})
The problem is that I don't need to run all requests in parallel lie Promise.all or async.foreach, but I rather need to call each promise sequentually and if condition is true I need to break out the loop and do not make any further requests as far as I have found a reachable server.
Please suggest what is the correct way to handle this use case. I have been searching for a long time, but still haven't found any good solution.
Thanks
EDIT
Sorry, yeah NetworkUntils.checkIfReachable() returns promise
Assuming NetworkUntils.checkIfReachable() is actually asynchronous (which is the only context in which this question makes sense and seems likely in nodejs) and assuming that NetworkUntils.checkIfReachable() returns a promise or can easily be changed to return a promise, then you can do the following:
findFirstReachableServer(ipAddresses) {
if (!ipAddresses || !ipAddresses.length) {
return Promise.reject(new Error("No servers ips provided"));
}
let agentOptions = {...};
let index = 0;
function next() {
if (index < ipAddresses.length) {
let ipAddress = ipAddresses[index++];
return NetworkUtils.checkIfReachable(agentOptions, ipAddress).then(function(isReachable) {
if (!isReachable) {
return next();
} else {
return ipAddress;
}
})
} else {
return new Error("No working servers found");
}
}
return Promise.resolve().then(next);
}
This function returns a promise that resolves with the ipAddress if a reachable one was found. It rejects if either no addresses were passed in, no reachable address was found or if NetworkUtils.checkIfReachable() rejects for any internal reason.
Note, to run non-blocking async operations in sequence, you can't use a normal for loop because each individual operation won't block so the for loop won't wait for them (it will just run to completion before any operations have finished). As such, you have to use a different method of sequencing. There are lots of different ways to do that. Since you don't necessarily want to run the entire sequence, I've chosen a manual sequencing where you control whether the next iteration is called or not.
Given you tagged the question async-await, using that is not a bad idea indeed:
async function findWorkingServer(ipAddresses) {
if (ipAddresses.length > 0) {
for (const currentServer of ipAddresses) {
const agentOptions = { … };
const isReachable = await NetworkUtils.checkIfReachable(agentOptions, ip);
// We do "block" here ^^^^^
if (isReachable) {
return currentServer;
}
}
throw new Error("No working servers found");
} else {
return new Error("No servers ips provided"); // I think you meant to `throw` here
}
}
Is there a way to make Node.js stream as coroutine.
Example
a Fibonacci numbers stream.
fibonacci.on('data', cb);
//The callback (cb) is like
function cb(data)
{
//something done with data here ...
}
Expectation
function* fibonacciGenerator()
{
fibonacci.on('data', cb);
//Don't know what has to be done further...
};
var fibGen = fibonacciGenerator();
fibGen.next().value(cb);
fibGen.next().value(cb);
fibGen.next().value(cb);
.
.
.
Take desired numbers from the generator. Here Fibonacci number series is just an example, in reality the stream could be of anything a file, mongodb query result, etc.
Maybe something like this
Make the 'stream.on' function as a generator.
Place yield inside the callback function.
Obtain generator object.
Call next and take the next value in stream.
Is it at-least possible if yes how and if not why? Maybe a dumb question :)
If you don't want to use a transpiler (e.g. Babel) or wait until async/await make it to Node.js, you can implement it yourself using generators and promises.
The downside is that your code must live inside a generator.
First, you can make a helper that receives a stream and returns a function that, when called, returns a promise for the next "event" of the stream (e.g. data).
function streamToPromises(stream) {
return function() {
if (stream.isPaused()) {
stream.resume();
}
return new Promise(function(resolve) {
stream.once('data', function() {
resolve.apply(stream, arguments);
stream.pause();
});
});
}
}
It pauses the stream when you're not using it, and resumes it when you ask it the next value.
Next, you have a helper that receives a generator as an argument, and every time it yields a promise, it resolves it and passes its result back to the generator.
function run(fn) {
var gen = fn();
var promise = gen.next().value;
var tick = function() {
promise.then(function() {
promise = gen.next.apply(gen, arguments).value;
}).catch(function(err) {
// TODO: Handle error.
}).then(function() {
tick();
});
}
tick();
}
Finally, you would do your own logic inside a generator, and run it with the run helper, like this:
run(function*() {
var nextFib = streamToPromises(fibonacci);
var n;
n = yield nextFib();
console.log(n);
n = yield nextFib();
console.log(n);
});
Your own generator will yield promises, pausing its execution and passing the control to the run function.
The run function will resolve the promise and pass its value back to your own generator.
That's the gist of it. You'd need to modify streamToPromises to check for other events as well (e.g. end or error).
class FibonacciGeneratorReader extends Readable {
_isDone = false;
_fibCount = null;
_gen = function *() {
let prev = 0, curr = 1, count = 1;
while (this._fibCount === -1 || count++ < this._fibCount) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
return curr;
}.bind(this)();
constructor(fibCount) {
super({
objectMode: true,
read: size => {
if (this._isDone) {
this.push(null);
} else {
let fib = this._gen.next();
this._isDone = fib.done;
this.push(fib.value.toString() + '\n');
}
}
});
this._fibCount = fibCount || -1;
}
}
new FibonacciGeneratorReader(10).pipe(process.stdout);
Output should be:
1
1
2
3
5
8
13
21
34
55
I'm building my first node.js application on my Raspberry Pi which I am using to control an air conditioner via LIRC. The following code is called when you want to increase the temperature of the AC unit. It sends a LIRC command every 250 milliseconds depending on how many degrees you want to increase it by. This code works as expected.
var iDegrees = 5;
var i = 0;
var delay = 250 // The delay in milliseconds
function increaseTemperatureLoop(){
i++;
//lirc_node.irsend.send_once("ac", "INCREASE", function() {});
console.log(i);
// Call the fucntion/loop again after the delay if we still need to increase the temperature
if (i <= iDegrees){
timer = setTimeout(increaseTemperatureLoop, delay);
}
else {
res.json({"message": "Success"});
}
}
// Start the timer to call the recursive function for the first time
var timer = setTimeout(increaseTemperatureLoop, delay);
I'm having a hard time working with the asynchronous nature of node.js. Once my recursive function is done, I return my json to the browser as shown in the code above. By habit, I feel like I should return the json in a line of code after my initial function call like below but obviously that wouldn't wait for all of the LIRC calls to be successful - it seems silly to have it inside of the function:
var timer = setTimeout(increaseTemperatureLoop, delay);
res.json({"message": "Success"});
What if I have a bunch of other stuff to do after my LIRC sends are done but before I want to send my json back to the browser? Or what if that block of code throws an error...
My second question is, how do I properly wrap the LIRC call in a try/catch and then if there is an error, stop the recursive calls, pass the error back up, and then pass this back to the browser along with the actual error message:
res.json({"message": "Failed"});
For track end of the cycle execution task, you can use a callback.
In order to know whether completed all routine tasks, you can use the task queue.
Monitor and report bugs to the top - it is possible with the help of
three of the same callback.
In general, it is desirable to wrap everything into a single object.
Some example for reflection:
var lircTasks = function __self (){
if (typeof __self.tasks === "undefined") __self.tasks = 0;
__self.func = {
increaseTemperature: function() {
// lirc_node.irsend.send_once("ac", "INCREASE_TEMPERATURE", function() {});
},
increaseFanPower: function() {
// lirc_node.irsend.send_once("ac", "INCREASE_FANPOWER", function() {});
}
}
var fab = function () {
__self.tasks++;
this.i = 0;
this.args = arguments[0];
this.callback = arguments[1];
this.run = function __ref(taskName) {
if (taskName) this.taskName = taskName;
if (this.i<this.args.deg) {
try {
__self.func[this.taskName]();
} catch(e) {
__self.tasks--;
this.callback( {message: "error", error: e, taskName: this.taskName, task: this.args, tasks: __self.tasks} );
}
this.i++;
setTimeout( __ref.bind(this), this.args.delay );
} else {
__self.tasks--;
this.callback({message:"complete", taskName: this.taskName, task: this.args, tasks: __self.tasks});
}
}
}
if ((arguments.length === 2) && (typeof arguments[1] === "function") && arguments[0].deg>0 && arguments[0].delay>=0) {
return new fab(arguments[0], arguments[1]);
}
}
function complete(e) {
console.log(e);
if (e.tasks === 0) console.log({message: "Success"});
}
lircTasks( {deg: 10, delay:100, device: "d1" }, complete ).run("increaseTemperature");
lircTasks( {deg: 20, delay:150, device: "d2" }, complete ).run("increaseTemperature");
lircTasks( {deg: 5, delay:100, device: "d3" }, complete ).run("increaseFanPower");