Async Use of Http Get in Node - node.js

For each url in a list of urls, I'm trying to push a string response from that url to an array after requesting it. This basically maps a list of urls to their contents. The catch is, the resulting list must be in the same order as the urls. This is taken from this challenge.
I can't figure out why my solution doesn't work. As far as I can tell, it should put things in the right order. But it doesn't pass the tests.
var http = require('http')
var urls = process.argv.slice(2, process.argv.length)
var result = Array(3)
urls.forEach((url, ind) => {
var str = ''
http.get(url, response => {
response.on('data', d => {
str+=d.toString()
})
response.on('end', () => {
result[ind] = str
})
if (result.every(val => {return val.length!==0})){
result.forEach(s => {console.log(s)})
}
})
})
Here are the workshopper's test results:
ACTUAL: "She'll be right slaps how he's got a massive jillaroo. As busy as a esky flamin built like a hottie. "
EXPECTED: "You little ripper shag on a rock and trent from punchy bottlo. As cunning as a compo no dramas lets get some gyno. Lets get
some durry when built like a cranky. "
ACTUAL: "You little ripper shag on a rock and trent from punchy bottlo. As cunning as a compo no dramas lets get some gyno. Lets get
some durry when built like a cranky. "
EXPECTED: "She'll be right slaps how he's got a massive jillaroo. As busy as a esky flamin built like a hottie. "
ACTUAL: "She'll be right slaps how he's got a massive jillaroo. As busy as a esky flamin built like a hottie. "
EXPECTED: "We're going no dramas heaps trent from punchy christmas. As busy as a ironman mate stands out like a thingo. As dry
as a cream to stands out like a rip snorter. Watch out for the ratbag
with gutful of cut lunch. Shazza got us some shag on a rock with trent
from punchy throw-down. "
ACTUAL: ""
EXPECTED: ""

Related

Pass large array of objects to RabbitMQ exchange

I receive large array of objects from an external source (about more than 10 000 objects). And then I pass it to exchange in order to notify other microservices about new entries to handle.
this._rmqClient.publishToExchange({
exchange: 'my-exchange',
exchangeOptions: {
type: 'fanout',
durable: true,
},
data: myData, // [object1, object2, object3, ...]
pattern: 'myPattern',
})
The problem is that it's bad practice to push such large message to exchange, and I'd like to resolve this issue. I've read articles and stackoverflow posts about that to find code example or information about streaming data but with no success.
The only way I've found out is to divide my large array into chunks and publish each one to exchange using for ... loop. Is it good practice? How to determine what length should each chunk (number of objects) have? Or maybe is there another approach?
It really depends on the Object size.. That's a thing you would have to figure out yourself. Get your 10k objects and calculate an average size out of them (Put them as json into a file and take fileSize/10'000 that's it. Maybe request body size of 50-100kb is a good thing? But that's still up to u ..
Start with number 50 and do tests. Check the time taken, bandwidth and everything what makes sense. Change chunk sizes from between 1-5000 and test test test . At some point, you will get a feeling what number would be good to take! .
Here's some example code of looping through the elements:
// send function for show caseing the idea.
function send(data) {
return this._rmqClient.publishToExchange({
exchange: 'my-exchange',
exchangeOptions: {
type: 'fanout',
durable: true,
},
data: data,
pattern: 'myPattern',
})
}
// this sends chunks one by one..
async function sendLargeDataPacket(data, chunkSize) {
// Pure functions do prevent headache
const mutated = [...data]
// send full packages aslong as possible!.
while (mutated.length >= chunkSize) {
// send a packet of chunkSize length
await send(mutated.splice(0, chunkSize))
}
// send the remaining elements if there are any!
if(mutated.length > 0) {
await send(mutated)
}
}
And you would call it like:
// that's your 10k+ items array!.
var myData = [/**...**/]
// let's start with 50, but try out all numbers!.
const chunkSize = 50
sendLargeDataPacket(chunkSize).then(() => console.log('done')).catch(console.error)
This approach send one packet after the other, and may take some time since it is not done in parallel. I do not know your requirements but I can help you writing a parallel approach if you need..

Discord.js, battle bot editing the message takes too much time

I am making a battle bot using discordjs and I'm having some difficulties in updating the message according to the logs that my bot created for the battle. Here's the current code I'm using.
let interval = setInterval(async () => {
if(turnCounter <= battle.logs.length){
let currPlayer = turnCounter % 2 === 0 ? p1 : p2;
embed
.setTitle(("Round ", turnCounter))
.setDescription(`
**Round Summary** \n
${currPlayer.toString()} used **${battle.textlogs[turnCounter][turnCounter % 2][0].name}** and \ndealt **${battle.textlogs[turnCounter][turnCounter % 2][0].damage}** \nand \nhealed **${battle.textlogs[turnCounter][turnCounter % 2][0].heal}**!`);
embed.fields[0] = {name: "Player 1", value: `${message.author.username} \n Class: ${battle.player1.class.name} \n HP: **${battle.health_log[turnCounter][0]}** (${battle.health_log[turnCounter][0] - battle.health_log[turnCounter-1 >= 0 ? turnCounter-1 : 0][0]})`, inline: true};
embed.fields[1] = {name: "Player 2", value: `${user.username} \n Class: ${battle.player2.class.name} \n HP: **${battle.health_log[turnCounter][1]}** (${battle.health_log[turnCounter][1] - battle.health_log[turnCounter-1 >= 0 ? turnCounter-1 : 0][1]})`, inline: true};
let t = Date.now();
m = await m.edit(embed);
console.log(`It took ${Date.now()-t}ms for request to travel!`);
turnCounter++;
}
else clearInterval(interval);
}, 1000);
This approach resulted in the following log:
It took 179ms for request to travel!
It took 1879ms for request to travel!
It took 1045ms for request to travel!
It took 197ms for request to travel!
There are two problems that I'm facing here.
1 ) I'm waiting 1000ms to perform the code and awaiting message update takes 200-2000ms. It's too much. I want it to be around 200ms all time. Total of 1000+200ms is pretty okay. How should I deal with this?
2 ) When there are many concurrent battles happening, they all wait each other's await to continue execution. If I'm supposed to await these messages, I need to use clusters to fork the process but when I fork, multiple discordjs instances open up and bot connects to discord for several times and responds to commands several times too. How am I supposed to handle this problem?
My attempts:
Current one
While loop
I tried to make in a while loop instead of setInterval however it behaved the same.
Not awaiting
I tried m = m.edit(embed) instead of m = await m.edit(embed), this time it suddenly ended the battle and sent the last battle log to the discord.

Google Actions: in dialogflow stop all mediaObjects

I have a Google Action that plays some pre-recorder stories. I use mediaObject to play them. Something like:
conv.ask(new MediaObject({
name: title,
url: storia,
description: autore,
image: new Image({
url: largeImageUrl,
alt: title,
}),
}));
I since some stories are longer than 4 minutes, I've split them in two, and use a media status control:
app.intent('Media Status', (conv) => {
const mediaStatus = conv.arguments.get('MEDIA_STATUS');
if (mediaStatus && mediaStatus.status === 'FINISHED') {
console.log(`track finished: ${conv.user.storage.track}`);
console.log(`storia2: ${conv.data.storia2}`);
if (conv.data.storia2 !== undefined) {
secondaParte = `<audio src="` + randomPhrase(continuaFavola) + `">
<desc>Vediamo come va a finire...</desc>
</audio>`;
storia = conv.data.storia2;
conv.data.storia2 = undefined;
conv.ask(new SimpleResponse('<speak>' + secondaParte +'</speak>'));
} else {
conv.close(new SimpleResponse('<speak>' + goodnight +'</speak>'));
}
} else {
conv.ask('Media Status ignoto');
}
});
It works.
The problem is that sometimes, while the media object is playing, if I say the "stop" command, the media object stops, but the stop intent is not triggered (it should say "Ok, goodbye" and close the action), so the action remains waiting for a prompt. A second stop actually execute the stop intent. So basically I need to stop it twice.
In the log I can't see any trace of the first stop command (that has been clearly received since the media object has stopped), only the second one.
Even worse, when I play the second half of the story, this time the command is supposed to close:
conv.close(new MediaObject({ ... );
If I say "OK Google, stop", instead of stopping the playback, the action ends but the media object keeps playing (I know the action is quit because I can give standard commands, like "what's the time" or "set an alarm".
What a mess.
So is there a way to close the action, and force stop of any currently playing media object?

Troubles processing data from Async calls

So this is my task:
You must collect the complete content provided to you by each of the
URLs an d print it to the console (stdout). You don't need to print
out the length, just the data as a String; one line per URL. The catch
is that you must prin t them out in the same order as the URLs are
provided to you as command-line arguments.
And I'm doing it like this:
var http = require('http');
var dataStream = [];
var dataArr = [];
var count = 0;
/*
Function to print results
#dataArr - array
*/
function printResults(dataArr) {
for (var i = 0; i < process.argv.length - 2; i++)
console.log(dataArr[i]);
}
/*
Function to get data from http
#i - int
Getting command line arguments as parametrs.
*/
function httpGet(i) {
http.get(process.argv[2 + i], function(res) {
res.setEncoding('utf8');
res.on('data', function(data) {
dataStream.push(data);
});
res.on('end', function() {
dataArr[i] = (dataStream.join(""));
dataStream = [];
count++;
if (count == process.argv.length - 2) {
printResults(dataArr);
}
});
res.on('error', function(e) {
console.log("Got error: " + e.message);
});
});
}
for (var i = 0; i < process.argv.length - 2; i++) {
httpGet(i);
}
And for some reason sometimes it stores data in array as it supposed, but sometimes it breaks and outputs complete nonsense.
Some results examples:
When working:
$ learnyounode verify program.js
Your submission results compared to the expected:
────────────────────────────────────────────────────────────────────────────
────
1. ACTUAL: "Shazza got us some trackies when as stands out like dog's ba
lls. Grab us a show pony heaps he hasn't got a lurk. She'll be right rubbish
mate it'll be budgie smugglers. You little ripper bloke heaps we're going t
op end. He's got a massive bog standard also built like a freckle. "
1. EXPECTED: "Shazza got us some trackies when as stands out like dog's ba
lls. Grab us a show pony heaps he hasn't got a lurk. She'll be right rubbish
mate it'll be budgie smugglers. You little ripper bloke heaps we're going t
op end. He's got a massive bog standard also built like a freckle. "
2. ACTUAL: "As dry as a sook and as dry as a cleanskin. As cunning as a
metho where get a dog up ya parma. "
2. EXPECTED: "As dry as a sook and as dry as a cleanskin. As cunning as a
metho where get a dog up ya parma. "
3. ACTUAL: "Gutful of gyno how come a mokkies. It'll be clacker and buil
t like a holy dooley!. Get a dog up ya boozer heaps come a captain cook. "
3. EXPECTED: "Gutful of gyno how come a mokkies. It'll be clacker and buil
t like a holy dooley!. Get a dog up ya boozer heaps come a captain cook. "
4. ACTUAL: ""
4. EXPECTED: ""
And an example when it's not working:
$ learnyounode verify program.js
Your submission results compared to the expected:
────────────────────────────────────────────────────────────────────────────
────
1. ACTUAL: "of bogan with it'll be rort. He hasn't got a give it a burl
flamin you little ripper dinky-di. Watch out for the mate's rate to shazza g
ot us some swag. "
1. EXPECTED: "He's got a massive op shop to you little ripper corker. Gutf
ul of bogan with it'll be rort. He hasn't got a give it a burl flamin you li
ttle ripper dinky-di. Watch out for the mate's rate to shazza got us some sw
ag. "
2. ACTUAL: "You little ripper thongs when as stands out like ropeable. T
rent from punchy boardies bloody as cunning as a brisvegas. "
2. EXPECTED: "You little ripper thongs when as stands out like ropeable. T
rent from punchy boardies bloody as cunning as a brisvegas. "
3. ACTUAL: "As dry as a uluru when come a scratchy. Flat out like a ute
with get a dog up ya chrissie. As busy as a fair go no worries it'll be fair
dinkum. She'll be right freo when it'll be cracker. He's Watch got out a fo
r massive the op crook shop my to as you busy little as ripper a corker. bru
mby. Gutful "
3. EXPECTED: "As dry as a uluru when come a scratchy. Flat out like a ute
with get a dog up ya chrissie. As busy as a fair go no worries it'll be fair
dinkum. She'll be right freo when it'll be cracker. Watch out for the crook
my as busy as a brumby. "
4. ACTUAL: ""
4. EXPECTED: ""
Don't treat the data you receive on res.on("data") as an array. Instead, treat it as a string and define it as a variable within the http function (not as a global variable) and do str += data.
Alternatively, you could look at using a library like Async to manage the correct ordering of the async functions you need to execute, as you need each Async to be executed and returned in sequential order.
So the problem was:
Your code makes the assumption that the 3 http responses will not
overlap - that a data event for one response will never occur before
the end event of the previous response, this is not always the case. I
recommend you move the definition of your dataStream variable inside
your httpGet function, this way each request/response will have its
own variable and they cannot interfere with each other, regardless of
timing.
So I refactored my solution to look like this, and now it works 100% of the time:
var http = require('http');
var dataArr = [];
var count = 0;
/*
Function to print results
#dataArr - array
*/
function printResults(dataArr) {
for (var i = 0; i < process.argv.length - 2; i++) {
console.log(dataArr[i].replace('undefined', ''));
}
}
/*
Function to get data from http
#i - int
Getting command line arguments as parametrs.
*/
function httpGet(i) {
http.get(process.argv[2 + i], function(res) {
res.setEncoding('utf8');
res.on('data', function(data) {
dataArr[i] += data;
});
res.on('end', function() {
count++;
if (count == process.argv.length - 2) {
printResults(dataArr);
}
});
res.on('error', function(e) {
console.log("Got error: " + e.message);
});
});
}
for (var i = 0; i < process.argv.length - 2; i++) {
httpGet(i);
}
More info: https://github.com/nodeschool/discussions/issues/1270

Xively and Node-Red

I'm fairly new at all this but have muddled my way to getting my Arduino to post values to a Xively stream I've named "Lux and Temp." Three values; count, lux, and temperature.
Now what I want to do is take those values and do something with them using Node-Red.
http://nodered.org
I have Node-Red up and running, but I'll be danged if I can figure out how to parse the data from the Xively API feed. https://api.xively.com/v2/feeds/1823833592
Sadly, I don't have enough reputation points to be able to actually post the data it returns to here, since it has more than 3 URLs embedded in the data. It's a LONG string of data too. ;)
I'm just stumped though as to how to write the function to extract the parts I want.
My initial want is to make a simple Twitter feed out of it. Something like;
"Count 40, Lux 30, Temp 78.3"
I'll eventually want to recycle the code for other things like making my RasPi do something; maybe a display or some LEDs. In either case I need to parse the data and build various messages with it.
Anybody have any experience with the Node-Red functions that can walk me through a solution? The Node-Red site is pretty awesome, but I think it assumes I'm a MUCH more experienced user than I really am. It gives hints, but frankly about all I know is fairly basic Arduino and trivial level Python.
OK, it shouldn't be too tricky, but try putting this in a function block:
var newPayload = "";
var count, lux, temp;
var data = msg.payload.datastreams;
for (var i = 0 ; i< data.length; i++) {
if (data[i].id === 'Count') {
count = data[i].current_value;
}else if (data[i].id === 'Lux') {
lux = data[i].current_value;
} else if (data[i].id === 'Temp') {
temp = data[i].current_value;
}
}
newPayload = util.format('Count: %s, Lux: %s, Temp: %s', count, lux, temp);
msg.payload = newPayload;
return msg;
You may need to add a msg.payload = JSON.parse(msg.payload); to the start if however your getting the feed from xively is not already considered to be json.
[edit]
You could also just run the flow through a JSON parse node. (I always forget the converter nodes)
You should be able to wire that to a twitter output node.

Resources