Asynchronous Function in Iteration - javascript - javascript

I am trying not to replicate code and loop over a a function in d3 that is asynchronous. Here is some code
Since d3.text is asynchronous , I am not able to use the index u in a correct way to append objects to the DOM. How should I go about this? I need the loop to go to next iteration once d3.text finished
for(var u in urls) {
console.log(u);
var url = "interest_points/" + urls[u] + ".csv";
var data_gpBy_month = {};
var sortable_month = []
d3.text(url, function(text) {
// some code...
var data = d3.csv.parseRows(text).map(function(row) {
//some code...
});
//some code
});
}

Something like this (fiddle: http://jsfiddle.net/EYAYT/2/) ?
var urls = ["asd", "asdasd", "Asdasfa"];
var currentUrlIndex = 0;
var getUrl = function(){
if (currentUrlIndex >= urls.length){
return null;
} else {
return "interest_points/" + urls[currentUrlIndex++] + ".csv";
}
}
var execd3Text = function(){
var url = getUrl();
if (url){
d3.text(url, function(text) {
//some code;;
execd3Text();
});
}
}
execd3Text();

The loop should simply become this:
for(var u in urls) { loadParseAndRender(u); }
All your existing logic then moves into loadParseAndRender, but at this point u will never get overridden. I.e, in fancy terms, it gets captured in the closure.
function loadParseAndRender(u) {
// the rest of your code
}
What David W suggested is the same thing as abive, but without creating a named function for it, you'd do this:
for(var _u in urls) {
(function(u) { // this is an anonymous function
// the rest of you code
})(_u) // this function gets called as soon as it's declared
}

If I understood properly:
function doSomething(array) {
var u = array.shift();
console.log(u);
var url = "interest_points/" + urls[u] + ".csv";
var data_gpBy_month = {};
var sortable_month = []
d3.text(url, function(text) {
// some code...
var data = d3.csv.parseRows(text).map(function(row) {
//some code...
});
//some code
if (array.length > 0)
doSomething(array);
});
doSomething(Object.keys(urls));

Related

How to update a global variable inside a nested function in angularjs?

This is the code I am using. currentId is 10; I am making a service call which has a function of $http.get(to a JSON) and I want to attack the array in JSON's length to currentId after the function is executed. How do I do it? Is there any special function in angularjs which helps me. I have read the other relevant questions here, but I need this done in angularjs. Thanks.
var currentId = 10;
console.log(currentId + ' before function'); //outputs 10
function findId(){
readJson.readJsonfun().then(function(data) {
currentId = data.length; //say data.length = 20;
return currentId;
});}
findId();
console.log(currentId + ' before function'); //should output 20?
As it's Async function - use async approach
var currentId = 10;
var modifiedId1;
var modifiedId2;
console.log(currentId + ' before function call');
function findId(){
return readJson.readJsonfun().then(function(data) {
currentId = data.length;
return $q.when(currentId);
});
}
findId().then(function(id){
//first time we need it
firstFunction(currentId); //or firstFunction(id);
});
function firstFunction(id){
modifiedId1 = id * 2;
}
function secondFunction(){
return findId().then(function(id){
//second time we need it, currentId is updates
modifiedId2 = id * 4;
return $q.when();
});
}
Store the variable of concern (currentID) in the service. Inside your service, it would look something like this:
.service('Example', function(){
var service = this;
var currentID = null;
service.getCurrentID = function() {
return currentID;
}
service.findId(){
readJson.readJsonfun().then(function(data) {
currentId = data.length; //say data.length = 20;
});}
Then, from outside of the service, you could call Example.findID() to update the currentID, and then Example.getCurrentID() to retrieve the actual value

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

Why does the second loop execute before the first loop?

so this might be a repost, but I don't really know how to explain my second problem.
I have this code:
var paragraphsArray = new Array();
function setParagraphs(offSet)
{
offSet = offSet * 12;
for (var i = 1; i < 13; i++)
{
var parX = i + offSet;
var testASd = $.get('php/entryParagraphs.php', {idd: parX}).done(function(paragraph)
{
//clear paragraph1 div
document.getElementById("paragraph1").innerHTML = "";
//create p elements
var pElem = document.createElement("p");
pElem.setAttribute("id", "pEntry"+i);
document.getElementById("paragraph1").appendChild(pElem);
$("pEntry"+i).text(paragraph);
});
}
}
edited: I removed the second loop because it was unnecessary, for some reason the p element creation starts on i==13, which is the extra one that shouldn't even do.
for some reason the second loop executes first, so the paragraphArray is printed out as undefined. I managed to "fix" the order with the setTimeout() function, BUT I still get the undefined message, instead of the value. In the first loop the value is printed out fine, but if I try and put it in a $("p").text(paragraph); I also get undefined. So although I was right about the execution order, the problem is still there!
Because first is in ajax call, declare paragraphsArray in global space and use a callback function, try this:
*Updated
var paragraphsArray = [];
function setParagraphs(offSet) {
offSet = offSet * 12;
var request = 0;
for (var i = 1; i < 13; i++) {
var parX = i + offSet;
var testASd = $.get('php/entryParagraphs.php', {idd: parX}).done(function(paragraph) {
request++;
paragraphsArray[request] = paragraph;
console.log(paragraphsArray[request]);
if (request === 12) {
alert('first');
callback();
}
});
}
}
function callback() {
for (var i = 1; i < 13; i++) {
console.log(paragraphsArray[i]);
}
alert('second');
}
Run the second loop inside of the first loop.
function setParagraphs (offSet) {
//paragraphs
var testing = 0;
var paragraphsArray = new Array();
offSet = offSet * 12;
for (var i=1;i<13;i++) {
var parX = i + offSet;
var testASd = $.get('php/entryParagraphs.php', { idd: parX }).done(function(paragraph) {
paragraphsArray[i] = paragraph;
console.log(paragraphsArray[i]);
alert('first');
for (var i=1;i<13;i++) {
console.log(paragraphsArray[i]);
alert('second');
}
});
}
}
$.get is async function. 1st cycle will just send requests and wouldn't wait for response, so 2nd cycle will start right after first, without getting response of $.get function. Thats why console.log(paragraphsArray[i]); in 2nd cycle shows undefined.
You only can handle response in first cylce.
You can use $("p").text(paragraph); only like in this example:
var testASd = $.get('php/entryParagraphs.php', { idd: parX }).done(function(paragraph) {
paragraphsArray[i] = paragraph;
console.log(paragraphsArray[i]);
alert('first');
$("p").text(paragraph);
});
You can't use variables, which are assigned in function
function(paragraph) {
paragraphsArray[i] = paragraph;
console.log(paragraphsArray[i]);
alert('first');
$("p").text(paragraph);
}
outside of this function.
To achieve what you want you have to use another approach.
HTML will be:
<div id='paragraphs'>
</div>
JS code:
var testASd = $.get('php/entryParagraphs.php', { idd: parX }).done(function(paragraph) {
$("#results").append("<p>"+paragraph+"</p>")
});
You should use ~ this code. I just show you approach.

Jquery modify html with each

I have a problem with jQuery. I want to write a function that dynamically replaces some content in my HTML.
This is the function:
function renderPage(datas, html) {
$(html).find("*").each(function(){
if ($(this).attr("data-func")) {
var df = $(this).attr("data-func");
var content = null;
eval("content = " + df + "($(this),datas);");
console.log(content);
}
});
}
The problem is, that it has no effect! I see in the log that the content variable is right, but the output is not different.
My function in eval:
function fill(element,data) {
element.html("BASSZA MEG");
var events = data.events;
var i = 0;
var n = events.length;
for (;i<n; i++) {
obj = events[i];
var tr = $("<tr>");
var nev = $("<td>");
var link = $("<a href='programbelso.html#id="+obj["event_id"]+"&logo="+obj["event_logo"]+"'>");
link.html(obj["event_name"]);
nev.append(link);
tr.append(nev);
element.append(tr);
}
console.log("element: " + element);
return element;
}
Firstly, do NOT loop over every single element in the document, and then use an if() statement. You can drastically improve the performance by using the right selector.
The following sample should achieve what you're trying to do without needing to use the evil eval() function, too:
function renderPage(datas, html) {
$('[data-func]').each(function() {
var df = $(this).attr("data-func");
var the_func = window[$(this).attr("data-func")];
if(typeof the_func === 'function') {
var content = the_func($(this), datas);
console.log(content);
}
});
}

Initialize Object with different parameters

Hello I have code which replaces document.write, makes a buffer and than pushes buffer into the document:
var lazyLoad = (function () {
var counter = 0
var buffer = new Array()
function work(options){
window.d = document
var tmp_buffer
d.write = d.writeln = function(s){ tmp_buffer += s}
d.open = d.close = function(){}
s = d.createElement('script')
s.setAttribute('type','text/javascript')
s.setAttribute('src',options.url)
d.getElementById(options.block).appendChild(s)
s.onload = function () {
buffer[counter] = tmp_buffer
console.log(buffer[1])
window.setTimeout(function() {
d.getElementById(options.block).innerHTML += buffer[counter]
}, 0)
counter++
}
}
return {
init: function (options) {
var CONFIG = {
url: '',
block: ''
}
$.extend(CONFIG, options)
random = $('#'+CONFIG.block).attr('rel')
id = $('#'+CONFIG.block).attr('id').replace(random,'')
id = id.replace('DIV','')
size = id.split('X')
ele_width = size[0] || CONFIG.width
ele_height = size[1] || CONFIG.height
$('#'+CONFIG.block).css({
'width':ele_width+'px',
'height':ele_height+'px'
})
$(window).load(function(){
if(options.adfox) {
random = $('#'+CONFIG.block).attr('id')
AdFox_getCodeScript(1, random, CONFIG.url)
}else{
work(options)
}
})
}
}
})();
If I init it once:
lazyLoad.init({
'http://test.com/test.js',
div1
})
But if I call it again with other parameters:
lazyLoad.init({
'http://test2.com/test.js',
div2
})
First init wont work. buffer will be empty. Where is my mistake?
I think that
$(window).load(function(){
will overwrite the event handler. Try using:
$(function(){
});
instead. I think it'll add an array of event handlers. I could be wrong though. Please let me know how it turns out.
Also, it doesn't look like you're defining "s" in the local scope. If you don't put "var" in front of a variable when you define it, it'll get created in the global scope.

Categories