Request in for loop - scope / responses out of order - node.js

After I perform a request to a .m3u8 Master Playlist Url like https://something.example.com/master.m3u8 with
request(input, function (error, response, body) {
...
}
and after some processing I get back a set of renditions, I store them into an object with some other things prepared, result is like
var set = {
input_url:'https://something.example.com/master.m3u8',
renditions:[
{
id:0,
url:'https://something.example.com/master_264.m3u8',
chunks:[]
}, {
id:1,
url:'https://something.example.com/master_578.m3u8',
chunks:[]
}, {
id:2,
url:'https://something.example.com/master_928.m3u8',
chunks:[]
}]
};
So I have my set of renditions stored in order from lowest to highest rendition. Now I need to perform another request for each single rendition and store their response into the chunks of each rendition.
Problem: After the first callback I get an error message TypeError: Cannot read property 'chunks' of undefined - Here is my code
for (var i = 0; i < set.renditions.length; i++) {
var done = false;
request(set.renditions[i].url, function(error, response, body) {
set.renditions[i].chunks.push(body);
completed_requests++;
if (completed_requests == set.renditions.length) {
return callback(null,set);
}
});
}
I believe this has something to do with a scope ? If am not referencing to original var set within the callback, but push the repsonses to another array, just as they come in, it works, but then they get stored out of order of course. So first the repsonse of 5th rendition, then 3rd rendition, then 4th rendition and so on (as it is an asynch operation) .
Any ideas how I should solve this, maybe something completely different ? Best practice ? Thanks !
=======================
First Try:
I tried using a forEach, but seems I don't get any result from it ...
function chunks (rendition_set, callback) {
var request_complete = false;
var completed_requests = 0
rendition_set.renditions.forEach ( function (entry) {
request(entry.url, function(error, response, body) {
var split = body.split('\n');
for (var i = 0; i < split.length; i++) {
if (split[i].indexOf('.ts') != -1 ) {
entry.chunks.push(split[i]);
}
}
completed_requests++;
});
})
while(request_complete == false) {
if (completed_requests == rendition_set.renditions.length) {
request_complete = true;
return callback(null,rendition_set);
}
}
}

So I ended up using async which basically handles all requests for all urls automatically, waits until everything is finished and gives me the result back in one package & in order the request was made ... which is pretty neat.
async.map(urls, httpGet, function (err, res) {
if (err) return console.log(err);
return callback (null, res)
});

Related

Issue with asynchronous mongodb query

I am trying to loop through an array and find the amount of tickets assigned to each person.
Unfortunately, I noticed that my taskcount is getting the same values but in different order, because of its asynchronous nature.
Some queries might take long and so the ones that gets finished first gets inserted and hence my array has the same values but in different order. Now, I want to avoid that and make it so, that once a query gets completed, only then the next value from the array is being picked up and pushed to search from the db. How can i modify my existing code.
exports.find_task_count = function(callback) {
var names = ['Evan', 'Surajit', 'Isis', 'Millie', 'Sharon', 'Phoebe', 'Angel', 'Serah']
var taskcount = []
var resultsCount = 0;
for (var i = 0; i < names.length; i++) {
_tasks.find({'assignee': names[i]}, function (err, tickets) {
resultsCount++
if (err) {
console.log(err)
return callback(err)
} else {
taskcount.push(tickets.length)
if (resultsCount === names.length) {
return callback(taskcount);
taskcount=[]
}
}
})
}
}
You can use the async module designed to handle such scenarios.
I have updated the code as follows
var async = require('async');
exports.find_task_count = function (callback) {
var names = ['Evan', 'Surajit', 'Isis', 'Millie', 'Sharon', 'Phoebe', 'Angel', 'Serah'];
async.map(names, function (name, iterateeCallback) {
_tasks.find({ 'assignee': name }, function (err, tickets) {
if (err) {
return iterateeCallback(err);
}
return iterateeCallback(null, tickets.length);
});
}, function (error, results) {
if (error) {
return callback(error);
}
return callback(null, results);
});
}
As per the documentation of async
Note, that since this function applies the iteratee to each item in parallel, there is no guarantee that the iteratee functions will complete in order. However, the results array will be in the same order as the original coll.
if you still want to process the array in series use mapSeries instead of map in the above code

Unable to send parameters to function Node.js

I have just started to learn nodejs. I have come across this problem please can anyone show me how to fix this?? I'm unable to send the parameter to the function. I just comes empty.
var getLocation = function(link, callback) {
console.log(link); //it comes empty | no value
if (cond.) {
console.log("stuff");
} else {
var location = value;
callback(location);
}
}
getTable(link) {
var session = obj.createSession();
session.table(id, 20, function(err, table) { //this block does some stuff work.
responseCb(link.id, link.jx, err, table);
console.log("extracted.");
});
getLocation(function(link, callback) { //this block sends links as parameter
console.log(callback);
});
}
function main() {
idList = [{
id: "",
jx: ""
}]; //contain data in the array.
for (var i = 0; i < idList.length; i++) {
getTable(idList[i]);
}
}
main();
Your getLocation function accepts two parameters: link and callback. When you call it however you only pass in a single argument:
getLocation(function(link, callback) { //this block sends links as parameter
console.log(callback);
});
You pass in a function that has the same parameters as the function that you are calling (getLocation). This doesn't make any sence. I assume that what you actually wanted to do was the following:
getLocation(link, function() {
console.log(location);
});
Pass the link to the function.
Pass a callback to the function.
In the callback, do something with the location.

Send response after call back functions execution completion in nodejs

I have a problem with call back functions and loops in nodejs how can I do so that the response should be send after the call back functions execution completion
app.post('/data', function(req,res){
var send = []
for (var i =0; i < p_objReq.body.quantity; i++) {
Instruments.count({//condetion}, function(err, count){
//using count and other parameters I am generating the code which is unique
Instruments.find({id: 'rondom_generated_code'},function(err, instrumentsCount){
if(instrumentsCount.length == 0){
send.push(rondom_generated_code)
if(send.length == p_objReq.body.quantity)
p_objRes.json({success : true, data : send})
}
else{
Instruments.count({//condetion}, function(err, count){
//using count and other parameters I am generating the code which is unique
send.push(rondom_generated_code)
if(send.length == p_objReq.body.quantity)
p_objRes.json({success : true, data : send})
})
}
})
})
}
})
when i wrote like this its sending the same random code that is last generated one. I tried removing the whole thing and written in function and called back but its also not working
One solution is to use Q.js, which is one of Promise library. More APIs of Q.js, please refer to the link in it. Here is one sample codes may could help you, If I catch you correctly for your question.
var Promise = require('Q');
app.post('/data', function(req,res){
var send = [];
var p = function () {
var deferred = Q.defer();
Instruments.count({condetion}, function(err, count){
//using count and other parameters I am generating the code which is unique
if (err) {
deferred.reject(new Error(error));
} else {
send.push(randomnumber)
}
});
return deferred.promise;
}
var ps = [];
for (var i =0; i < p_objReq.body.quantity; i++) {
ps.push(p);
}
Q.all(ps).then(function(){ res.json({success : true, data : send}); });
});

Node.js + Cheerio : Request inside a loop

I'm using cheerio, request and Node.js.
When I run the script below, it outputs names in a wrong order. I believe that it's caused by asynchronous nature of it, how can I make it work in the "right" order? Do I need to use a sync package or is there a way to change it in a way so it'll work in a sync way?
app.get('/returned', function (req, res) {
for (var y = 0; y < 10; y++) {
var url = "http://example.com" + y + "/person.html";
request(url, function (err, resp, body) {
$ = cheerio.load(body);
var links = $('#container');
var name = links.find('span[itemprop="name"]').html(); // name
if (name == null) {
console.log("returned null");
} else {
console.log(name);
}
});
}
});
Promise makes this relatively easy:
app.get('/returned', function (req, res) {
let urls = [];
for (let y = 0; y < 10; y++) {
urls.push('http://example.com' + y + '/person.html');
}
Promise.all(urls.map(function (url) {
return new Promise(resolve, reject) {
request(url, function (err, resp, body) {
if (err) {return reject(err);}
let $ = cheerio.load(body);
let links = $('#container');
let name = links.find('span[itemprop="name"]').html(); // name
resolve({name: name, links: links, url: url});
});
});
}).then(function (result) {
result.forEach(function (obj) {
if (obj.name == null) {
console.log(obj.url, "returned null");
} else {
console.log(obj.url, obj.name);
}
});
}).catch(function (err) {
console.log(err);
});
});
I started by creating an array of urls to get, then I mapped that to an array of promises. When each of the requests are complete, i resolved the promise with the name, url, and links. When all promises were complete, I then looped over the result which will will be in the original order. This runs in parallel.
Nope, you shouldn't have to use a sync package. IMO the cleanest way is to use a mature 3rd party library.
I'd recommend async.
The async.series method would execute all request functions in the order they are given, then allow you to register a callback to fire when all requests have been made, or when an error has occurred.
https://github.com/caolan/async#seriestasks-callback

WebRTC getStat() API Set UP

I am trying to use getStat() from WebRTC's api to see if it provides any useful info measure latency and other video streaming data. The problem is that there's not much info of how to use it.
Even older existing examples are pretty rare but the api has changed since then.
For example, my set up:
peerconnection.getStats(function(stats) {
console.log(stats); } ));
This returns a RTCStatsResponse object with 2 functions
RTCStatsResponse {result: function, namedItem: function}
Trying to call that result() function returns an array of RTCStatsReport objects with type 'googLibjingleSession' for the 1st object and type 'googTrack' for the 2nd object. The other nameItem function is undefined when trying to call it
[RTCStatsReport, RTCStatsReport]
From what little info available (https://groups.google.com/forum/#!topic/discuss-webrtc/fpr4yn4-3sg), I would be getting alot more RTCStatObjects with more useful info than I am currently getting.
Does anyone have experience with using webrtc's getStats? I believe I may not be doing this correctly
The following solution works for me.
Creating peer connection
pc = new RTCPeerConnection(pc_config, pc_constraints);
Adding onaddstream handler
pc.onaddstream = onRemoteStreamAdded;
The handler itself
var onRemoteStreamAdded = function(event) {
attachMediaStream(remoteVideo, event.stream);
remoteStream = event.stream;
getStats(pc);
};
Pay attention on the getStats function called from the handler, the function is following
function getStats(peer) {
myGetStats(peer, function (results) {
for (var i = 0; i < results.length; ++i) {
var res = results[i];
console.log(res);
}
setTimeout(function () {
getStats(peer);
}, 1000);
});
}
The myGetStats function is a wrapper to make it possible universal in different browsers;
function myGetStats(peer, callback) {
if (!!navigator.mozGetUserMedia) {
peer.getStats(
function (res) {
var items = [];
res.forEach(function (result) {
items.push(result);
});
callback(items);
},
callback
);
} else {
peer.getStats(function (res) {
var items = [];
res.result().forEach(function (result) {
var item = {};
result.names().forEach(function (name) {
item[name] = result.stat(name);
});
item.id = result.id;
item.type = result.type;
item.timestamp = result.timestamp;
items.push(item);
});
callback(items);
});
}
};
Every second it will get statistics and print raw object into console log. You can parse the log and then change the code, getting necessary object's field.

Resources