LearnYouNode: Juggling Async without BL/Async/After - javascript

I was following this tutorial when a wild step 9 appears.
This problem is the same as the previous problem (HTTP COLLECT) in that you need to use http.get(). However, this time you will be provided with three URLs as the first three command-line arguments.
You must collect the complete content provided to you by each of the URLs and 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 print them out in the same order as the URLs are provided to you as command-line arguments.
My code was (It doesn't work fine just when he pleases):
http = require("http");
var url = [process.argv[2], process.argv[3], process.argv[4]];
var responses = [];
var completed_responses = 0;
for(var i in url){
http.get(url[i], function(response){
var content = "";
//if(completed_responses == url.length){
response.setEncoding("utf-8");
response.on("data", function(data){
content += data;
})
response.on("error", console.error);
response.on("end", function(end){
console.log(content);
});
})
}
And the answer was:
var http = require("http");
var bl = require("bl");
var results = [];
var count = 0;
function printResults(){
for(var i = 0; i < 3; i++)
console.log(results[i]);
}
function httpGet(index){
http.get(process.argv[2 + index], function(response){
response.pipe(bl(function(err, data){
if (err)
return console.error(err);
results[index] = data.toString();
count++;
if(count == 3)
printResults()
}))
})
}
for(var i = 0; i < 3; i++)
httpGet(i);
What is the right answer WITHOUT BL/AFTER/ETC?
Thanks to all!

I've done that tutorial myself when I was first learning node and I remember that step of the tutorial. The solution was fairly underwhelming. Anyway, for your answer:
NodeJs Asynchronous programming - Coordinating parallel calls
You can check the code in the question and make the fixes I suggested in my answer. That should solve it without BL/Async/Whatever else that tutorial mentions.

Here is my code for the Juggling Async challenge without using any third-party libraries.
var http = require("http");
var urls = [process.argv[2], process.argv[3], process.argv[4]];
var urlResults = new Array("", "", "");
var allDoneCount = 0;
urls.forEach(function (_url) {
http.get(_url, function (resp) {
resp.on("data", function (data) {
if (_url === urls[0]) {
urlResults[0] += data.toString();
} else if (_url === urls[1]) {
urlResults[1] += data.toString();
} else {
urlResults[2] += data.toString();
}
})
resp.on("end", function () {
allDoneCount++;
if (allDoneCount === 3) {
console.log(urlResults[0]);
console.log(urlResults[1]);
console.log(urlResults[2]);
}
})
resp.on("error", function (err) {
console.log(err);
})
}).on("error", function (err) {
console.log(err);
})
})

This is how you can do it without any external modules(except http ;P).
const http = require('http'); //http module
let results = ["", "", ""]; //this will store the data from http.get()
let counter = 0; //to keep a counter for no of httpget's done
//it will iterate when counter is 3 i.e. the 'end' for all
function print() {
for (let i = 0; i < 3; i++) {
console.log(results[i]);
}
}
//accept index(for process.argv) as parameter
function httpGetter(i) {
//http.get method on the url first encountered, 2+i because 2 values are reserved
http.get(process.argv[2 + i], (res) => {
//for converting (res)ponse to string/alternatively toString() method can be used
res.setEncoding('utf8');
//event data on the url, callback with recived chunk as parameter
res.on('data', function(chunk) {
//appending the recived chunk to that element of results corresponding to 'i' of httpGetter function
results[i] += chunk;
});
//event end, when no more data is read
//runs every time for each value of 'i' that is for each url
res.on('end', function() {
//to keep count
counter++;
//when 3 that is when data from all inputs receved
if (counter === 3) {
//print function simply iterating over results array
print();
}
});
})
}
//inputs are recieved from here
for (let i = 0; i < 3; i++) {
//i can be index for results
httpGetter(i);
}

Related

Spotify API Getting all tracks at once

I am trying to do a web app that uses the Spotify API (using node.js as server) but I've recently come across a problem I can't solve. Basically, I want to compare the content of two playlists but since the API only gives 100 tracks per request, I can't find a way to get this done. I already tried to use loops inside requests but I doesn't seem to work.
request.get(options, function(error, response, body) {
var data = [];
data.push(JSON.parse(JSON.stringify(body)));
let urls = [];
for (let i = 0; i < data[0].total; i = i + data[0].limit){
urls.push('https://api.spotify.com/v1/me/tracks?offset='+i);
}
data = [];
for (let i = 0; i < urls.length; i++) {
options.url = urls[i];
request.get(options, function(error, response, body) {
let tmp = JSON.parse(JSON.stringify(body));
let items = tmp.items
if (items != null){
for(let o of tmp.items) {
data.push(o.track.name);
}
if (tmp.next == null){
return res.send(JSON.stringify(data));
console.log('done');
}
}
});
}
You could use recursion to get all tracks in a playlist.
You could have a method that gets the playlist page then loop call it until you don't have any more tracks to retrieve. Just as an example, here is a possible solution in pseudo code. I've written this quickly so forgive any bugs...
function getplaylisttracks(playlistid, pagesize,offset) {
//call 'get tracks' api with pagesize and offset
//return response
}
var offset = 0;
var pagesize = 100;
var continueloop = true;
var result = getplaylisttracks(YOURID, pagesize, offset);
do {
try {
//*** process result tracks into your array here ***
if(result.next!=null) {
offset = offset+pagesize;
result = getplaylisttracks(YOURID, pagesize, offset);
}
else {
continueloop = false;
}
}
catch(e) {
//handle error here...
continueloop = false;
}
}
while(continueloop);
Once you have all the tracks for each playlist you could run a comparison

sync call in tableau javascript

I am trying to do sync call using tableau.
var checkLen = $s_filter_i.length;
for (i = 0; i < checkLen; i++) {
var filterValfscr = $s_filter_i[i].VALUE;
Send_Tablo(filterValfscr);
}
When call this function Send_Tablo(filterValfscr) it runs the below code.
But the problem is before I get response form getSummaryDataAsync() it comes out of the function recalls Send_Tablo(filterValfscr) again I get the latest request data instead of first data request.
function Send_Tablo() {
var filter_indx = JSON.parse(window.localStorage.getItem('filter_indx'));
var arry = [];
for (var i = 0; i < filter_indx.s_filter_i.length; i++) {
Attr_lab_name = filter_indx.s_filter_i[i].ATTRIBUTELABEL;
arry.push(filter_indx.s_filter_i[i].VALUE);
}
currentViz.getWorkbook().changeParameterValueAsync('Attribute_Label', filterValfscr, Attr_lab_name, arry);
alert(filterValfscr);
var fScr_data;
sheet = currentViz.getWorkbook().getActiveSheet();
sheet.getSummaryDataAsync(options).then(function (t) {
data = t.getData();
console.log("Data", data);
frscr_demo();
});
function frscr_demo() {
//var fScr_data;
currentViz.getWorkbook().getActiveSheet().applyFilterAsync(Attr_lab_name, arry, tableau.FilterUpdateType.REPLACE);
sheet = currentViz.getWorkbook().getActiveSheet();
sheet.getSummaryDataAsync(options).then(function (t) {
fScr_data = t.getData();
console.log("FSCR", fScr_data);
var aa = $(fScr_data).length;
});
}
}
What I am try to achieve is Send_Tablo() should run all the Async function first before running the second iteration of Send_Tablo() from the for loop.
Do let me known what I am doing wrong? Thanks in advance.

Parse server save data in series

So we have large data in JSON format.
We want to save it to a class (table) in our Parse app.
I wrote a JS script which can read the file and go through the JSON data.
But when is do the saving it all gets messed up. Its loops in the first one for ever. I understand that there is something called promise bt I don't understand how to use it? Can anyone help. My code is given below.
function processJson(result) {
object = JSON.parse(result);
verbose.textContent = "Read " + object.results.length + " objects";
var count = object.results.length;
var countAc = 0;
logger("To save: " + count);
i = 0;
while (i < count) {
if (object.results[i].areaType == 'ac') {
save(i).then(function (object) {
i = i + 1;
logger("Success: " + object.id);
});
} else {
logger("ac not found");
i = i + 1;
}
}
}
function save(i) {
logger("ac found");
var constituency = new Constituency();
constituency.set("points", object.results[i].points);
constituency.set("areaType", object.results[i].areaType);
constituency.set("name", object.results[i].name);
constituency.set("state", object.results[i].state);
constituency.set("index", object.results[i].index);
constituency.set("pc", object.results[i].pc);
constituency.set("center", object.results[i].center);
constituency.set("oldObjectId", object.results[i].objectId);
return constituency.save();
/*constituency.save().then(function(obj) {
// the object was saved successfully.
i = i + 1;
logger("Success: " + obj.id);
}, function(error) {
// the save failed.
logger(error.message);
i = i + 1;
});*/
}
I would do something like that:
function processJson(result) {
var object = JSON.parse(result);
for (var i = 0; i < object.results.legnth; i++){
var parseObject = createParseObjectFromJSONObject(object.results[i]);
parseObject.save(null).then(function(object){
console.log("object saved: " + object.id);
},function(error){
console.log("error: " + error);
});
}
}
function createParseObjectFromJSONObject(jsonObject){
var constituency = new Constituency();
constituency.set("points", jsonObject.points);
constituency.set("areaType", jsonObject.areaType);
constituency.set("name", jsonObject.name);
constituency.set("state", jsonObject.state);
constituency.set("index", jsonObject.index);
constituency.set("pc", jsonObject.pc);
constituency.set("center", jsonObject.center);
constituency.set("oldObjectId", jsonObject.objectId);
return constituency;
}
You can do it even better..
You can first push all the parse objects into array and then call saveAll to save all the parse objects in one request. This solution is good for < 1000 records .. if you have more than 1000 then you can do paging (first 1000 and saveAll, other 1000 and saveAll ....)
In this version your code will look like this:
function processJson(result) {
var object = JSON.parse(result);
var allObjects = [];
for (var i = 0; i < object.results.legnth; i++){
var parseObject = createParseObjectFromJSONObject(object.results[i]);
allObjects.push(parseObject);
}
// outside the loop we are ready to save all the objects in
// allObjects array in one service call!
if (allObjects.length > 0){
Parse.Object.saveAll(allObjects).then(function(){
console.log("all objects were saved!");
// all object ids are now available under the allObjects array..
},function(error){
console.log("error: " + error);
});
}
}
function createParseObjectFromJSONObject(jsonObject){
var constituency = new Constituency();
constituency.set("points", jsonObject.points);
constituency.set("areaType", jsonObject.areaType);
constituency.set("name", jsonObject.name);
constituency.set("state", jsonObject.state);
constituency.set("index", jsonObject.index);
constituency.set("pc", jsonObject.pc);
constituency.set("center", jsonObject.center);
constituency.set("oldObjectId", jsonObject.objectId);
return constituency;
}
Good Luck :)

Callback is undefined and other stories

I got the following script, which is not working propperly. I know about getJSON's async nature, so I tried to build a callback function (jsonConsoleLog), which is supposed to be executed before getJSON get asigned to var (myJson = json;). After running debug in Chrome, I got two things out: A) debug is highlighting jsonConsoleLogcalls inside getJSON function as undefined.
B) Console is throwing TypeError: Cannot read property '0' of null for var friends = myJSON[0].friends;, which means the whole function doesn't work.
I'm in battle with it since saturday and I really don't know what to do. There's clearly something up with my callback function, but shoot me if I know what. Help?
var myJSON = null;
var main = document.getElementsByClassName('main');
var sec = document.getElementsByClassName('sec');
function getJSON(jsonConsoleLog){
$.getJSON('http://www.json-generator.com/api/json/get/cpldILZRfm? indent=2', function(json){
if (json != null){
console.log('Load Successfull!');
};
if (jsonConsoleLog){
jsonConsoleLog(json[0].friends);
}
myJSON = json;
});
};
function jsonConsoleLog(json) {
for (var i = 0; i < json.length; i++) {
console.log('friend: ' + friends[i]);
};
};
getJSON();
var friends = myJSON[0].friends;
function myFn1(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
function myFn2(){
for(var i = 0; i < friends.length; i++) {
main_div[i].innerHTML = friends[i].name;
};
};
main.innerHTML = myFn1();
sec.innerHTML = myFn2();
The first problem is because your function getJSON is expecting one formal argument, which you've called jsonConsoleLog. But you are not passing any arguments to getJSON. This means that inside getJSON the formal parameter, jsonConsoleLog, will indeed be undefined. Note that because you've named the formal parameter jsonConsoleLog, which is the same name as the function you're hoping to call, inside getJSON you won't have access to the function. What you need to do is pass the function as the parameter:
getJSON(jsonConsoleLog);
The second problem is I think to do with the json variable - it doesn't have a property 0 (i.e. the error is occurring when you try to treat it as an array and access element 0), which suggets that json is coming back empty, or is not an array.
you're calling getJSON without the callback parameter - therefore, the local variable jsonConsoleLog is undefined in getJSON
snip ...
function blah(json) { // changed name to avoid confusion in the answer - you can keep the name you had
for (var i = 0; i < json.length; i++) {
console.log('friend: ' + friends[i]);
};
};
getJSON(blah); // change made here (used the function name blah as changed above
var friends = myJSON[0].friends;
function myFn1(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
snip...
The issue with
var friends = myJSON[0].friends;
is duplicated here many many times ... $.getJSON is asynchronous and you are trying to use it synchronously
i.e. when you assign var friends = myJSON[0].friends; myJson hasn't been assigned in $.getjson ... in fact, $.getjson hasn't even BEGUN to run
here's all your code reorganised and rewritten to hopefully work
var main = document.getElementsByClassName('main');
var sec = document.getElementsByClassName('sec');
function getJSON(callback) {
$.getJSON('http://www.json-generator.com/api/json/get/cpldILZRfm? indent=2', function(json) {
if (json != null) {
console.log('Load Successfull!');
};
if (callback) {
callback(json);
}
});
};
function doThings(json) {
var friends = json[0].friends;
for (var i = 0; i < friends.length; i++) {
console.log('friend: ' + friends[i]);
};
function myFn1() {
for (var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
function myFn2() {
for (var i = 0; i < friends.length; i++) {
main_div[i].innerHTML = friends[i].name;
};
};
main.innerHTML = myFn1();
sec.innerHTML = myFn2();
}
getJSON(doThings);
Correct, fully working code (basically the same as accepted, correct answer but stylistycally bit different)
var main = document.getElementsByClassName('main');
var sec = document.getElementsByClassName('sec');
var friends = null;
function getJSON(jsonConsoleLog){
$.getJSON('http://www.json-generator.com/api/json/get/cpldILZRfm?indent=2', function(json){
if (json != null){
console.log('Load Successfull!');
};
if (jsonConsoleLog){
jsonConsoleLog(json[0].friends);
}
});
};
function jsonConsoleLog(json) {
for (var i = 0; i < json.length; i++) {
console.log('friend: ' + json[i]);
};
friends = json;
myFn1();
myFn2();
};
function myFn1(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML = friends[i].id;
};
};
function myFn2(){
for(var i = 0; i < friends.length; i++) {
main[i].innerHTML += friends[i].name;
};
};
getJSON(jsonConsoleLog);

Variable from for loop always returns 0

I am reasonably new to node.js / sails.js and have run into a problem that I know the answer is simple but I cannot seem to work it out.
My code is as follows
SendCompleted : function(req,res)
{
var updated = 0;
var params = req.params.all();
var dt = JSON.parse(decodeURI(params.id));
var connection = new sql.Connection(testrmis, function (err)
{
if (err) {
}
for(var i = 0; i < dt.length; i++) {
var obj = dt[i];
var request = new sql.Request(connection);
request.stream = true;
request.input('branchid', sql.Int(), obj.branch_id);
request.input('picklistid', sql.Int(), obj.picklist_id);
request.input('scanned',sql.Int(),obj.scanned);
request.input('expected', sql.Int(),obj.expected);
request.input('poscode', sql.VarChar(),obj.poscode);
request.input('label', sql.VarChar(), obj.label);
request.input('dt', sql.VarChar(), obj.dt);
request.execute('WAREHOUSE_InsertPiPackData');
request.on('done', function(returnValue) {
updated = updated + returnValue;
console.log(updated);
});
}
res.send("[{\"ReturnValue\":" + updated + "}]");
});
}
I am sending in 4 lines of results and my console.log(updated) counts up as it should for each line, e.g 1,2,3,4
However the res.send result for updated is always 0.
Could anyone please explain why this is happening? My var updated is outside of my loop and this is getting updated correctly, however when the loop is finished it seems to get reset to 0?
returnValue == ##rowcount from the stored procedure
request is async so
res.send("[{\"ReturnValue\":" + updated + "}]");
gets executed even before you get the callback on request as JS doesn't wait for the callback and executes the next line. What you can do is use a counter and place your res.send inside for loop.
SendCompleted : function(req,res)
{
var updated = 0;
var params = req.params.all();
var dt = JSON.parse(decodeURI(params.id));
var connection = new sql.Connection(testrmis, function (err)
{
if (err) {
}
var count = dt.length;
for(var i = 0; i < dt.length; i++) {
var obj = dt[i];
var request = new sql.Request(connection);
request.stream = true;
request.input('branchid', sql.Int(), obj.branch_id);
request.input('picklistid', sql.Int(), obj.picklist_id);
request.input('scanned',sql.Int(),obj.scanned);
request.input('expected', sql.Int(),obj.expected);
request.input('poscode', sql.VarChar(),obj.poscode);
request.input('label', sql.VarChar(), obj.label);
request.input('dt', sql.VarChar(), obj.dt);
request.execute('WAREHOUSE_InsertPiPackData');
request.on('done', function(returnValue) {
count--;
updated = updated + returnValue;
console.log(updated);
if(count == 0) res.send("[{\"ReturnValue\":" + updated + "}]");
});
}
});
}
Try for this:
May be Async problem:
for(var i = 0; i < dt.length; i++) {
//Your logic
if(i=== dt.length){
res.send("[{\"ReturnValue\":" + updated + "}]");
}
}
This is because at the time you do request.send, the value of updated is not incremented. This is because request.execute is asynchronous and done handler will be invoked after the res.send has been executed.
I would recommend a promise library (example, q). You can combine the promises and then use Q.all to do req.send when all the promises are done.
See more details here

Categories