Please excuse the awkward title I'll try my best to explain my peculiar problem.
I have three bits of javascript code:
Some self executing code which calls my personal Ajax function and passes it a callback.
The ajax function itself which retrieves the data calls the callback passing it the data.
The callback itself which takes in the data and parses it into an array of n length.
What should be noted is that the self executing code and the callback function are defined within their own closure. The ajax function is accessed through an imported named closure which I defined as $.
I am fairly new to JavaScript and I am still learning about closures and their scopes. I have reason to believe that this problem is probably related to that.
Anyway, my problem relates to trying to access that supposedly populated array. Since I defined the array in a suitable scope (or so I believe) as the parse function I think I should have no problem pushing items into it.
This is self exectuting :
(function ($){
//Load stock
var items = [];
var response = $.Ajax("scripts/Lookup.php","GET",parse);
function parse(a){
for(/*Simplified view*/){
var item = new $.Item();
item.name = domStuff.textContent;
item.description = domStuff.textContent;
item.price = domStuff.textContent;
item.id = domStuff.textContent;
items.push(item);
}
}
//Test the length (and verify contents)
for(var i=0; i < items.length; i++){
alert(items[i].price);
}
}($));
This is my definitions, which includes the Ajax function:
var $ = (function(){
var s = {};
var ajax = function(url,method,callback){
var a = new XMLHttpRequest();
a.open(method, url, true);
a.onreadystatechange = function(){
if(this.readyState==4){
callback(a);
}
}
a.send();
};
s.Ajax = (function(){
return ajax;
}());
return s;
}());
So what justifies the title is that when I probe the code with firebug, I can see that items is populated with 3 Objects correctly defined by the parsed data.
The loop then alerts as intended 3 times.
However, if I remove the break points and have firebug ignore the code then the loop doesn't play out and I can only assume that the array is empty.
The code works also if I alert(items) prior to the test loop.
AJAX is filling items asynchronously, meaning in your closure, on first execution items.length will be zero (from the original array initialization). Over time, AJAX through calls to your parse method will fill this array.
Because you're keeping items in a closure, you won't actually be able to access it from outside - which is a good practice, but can cause problems with debugging! Instead of items.push(item) try alert(item) (any browser, but execution blocking) or console.log(item) (firebug only) to see that you are, indeed, getting the results back from AJAX just not in initial execution.
I'm not a JavaScript expert by any means. However, I have run into situations on other programming languages where debugging has solved an issue.
In those cases, debugging has slowed the code down enough for it to work. Some process A needs asynchronous process B to return an object or do something before executing beyond a certain point. What is most likely happening in these types of scenarios is that the main thread is continuing or not waiting for the object to be populated by the other routine.
I hope that helps.
Your parse function is called asynchronously. You need to wait for the parse function to be called before testing it.
Either test the result in you parse function or have another function as the callback for ajax call. This function calls parse and then test the results.
Related
Considering the following recursive Fibonacci function that’s been optimized using memoization.
No other code apart from this.
function memoizeFibonacci(index, cache = []) {
if (cache[index]) {
return cache[index]
} else {
if (index < 3) return 1
else {
cache[index] = memoizeFibonacci(index - 1, cache) + memoizeFibonacci(index - 2, cache)
}
}
console.log(cache)
return cache[index];
}
memoizeFibonacci(6)
Can someone please explain how is the cache array updated? When viewing the console logs the cache seems to hold all the previous values from the resolved recursive functions. But to me, this doesn't make sense as the cache is not stored outside memoizeFibonacci so the scope should not allow this.
Every nested recursive function adds its item when it resolves only during the chain of executions of the recursion.
console.log is asynchronous and probably shows repeatedly the final result. There is no specification about how console.log work so it can act differently depending on the environment, it's I/O.
Even if you make it work as expected in your environment, it can work differently for other user. An hypothesis based on the use of console in your algorithm is not correct.
Kyle Simpson references:
Recursion in JS https://github.com/getify/Functional-Light-JS/blob/master/manuscript/ch8.md
Console log https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20%26%20performance/ch1.md#async-console
This has nothing to do with closures. This is simply passing the same array to nested recursive calls.
When you pass an array (or any object for that matter) to a function, the array is not copied, rather a reference to it is passed, so changing the array in the recursive call will affect the same array.
Here is an example of what is basically happening:
function foo(arr) {
arr[0] = "hello";
}
let arr = [];
foo(arr);
console.log(arr); // changed because of the call to foo
Notice that the recursive calls to memoizeFibonacci is explicitly passing the cache object as the second parameter, so each recursive call is sharing the same array as the top-level call, and any changes to the cache object in the recursive calls is reflected in the top-level call aswell.
BTW, this type of memoization is not persistent, meaning that these two calls:
memoizeFibonacci(6);
memoizeFibonacci(10);
don't share the same cache object. They each use a different cache array that must be reconstructed from scratch rather than the call to memoizeFibonacci(10) using the cache object already constructed in the call to memoizeFibonacci(6) and appending to it. A more efficient memoization makes use of closures like in this example: https://stackoverflow.com/a/8548823/9867451
Note: If you are asking why all the console.log(cache) print out the same exact filled array, that's because they are printing the same array, and the values you see in the console are not necessarily added at the point of the console.log. Take a look at this other question: array.length is zero, but the array has elements in it. To log exactly what's in the cache object at the time of the console.log, change it to:
console.log(JSON.stringify(cache));
I have a problem in that there are several functions and constructors spread out in the different parts of my script.
For instance, I have the following function, which generates the names for the nodes
let graphFactory = new GraphFactory();
function GraphFactory() {
let nodeNames = ['apple', 'orange'];
this.getNodeNames = function() {
return nodeNames;
}
}
However, this function is loaded after another script, which needs the value returned by graphFactory.getNodeNames().
However, when I run
let value = graphFactory.getNodeNames();
I obviously get an error, because graphFactory is declared later below and so it's not recognized. And even if it was, it's not guaranteed that the value I need would already be loaded.
How would I change all this to ensure:
1) value gets a value only after nodeNames in graphFactory is populated
2) That graphFactory is recognized even if it's declared later in the script?
As I understand I need to use promises, but even with the promises I don't quite get how I could structure this whole thing in the way that it would work?
I was also thinking to use async/await but I don't quite understand how to integrate it into my structure above.
Thank you for any help you can provide!
I am running into a problem when I am attempting to use a closure to maintain a value that I use upon a callback from an asynchronous function.
The basic order of operations for the functions is:
I am attempting to use google to route through several waypoints at once. So route from x->y->z in one call
If that fails then the routing is broken up into individual calls and routed through each of them. So x->y and then y->z.
The problem lies in the second part. There is a function that is being called in the single routing that is a synchronous function that leads to another synchronous function that leads to an asynchronous function. Then, when it completes it uses callbacks to bubble itself back up to the original function.
So for example: w would x which would call y which would call z (which is async). Then z would callback to y which would callback to x. When I arrive back in x however I know that the scope of w is gone. However, by using a closure I would expect to be able to retain these values. However, I am still getting the classic problem that closures fix where I only get the final value that a certain value was declared.
This is using ES5 javacript code. Unfortunately my company hasn't gotten to the step where we can use babel to compile on ES6 so an ES5 solution is necessary. I am using jquery and have tried moving the closure to the callback in the way of:
DatabaseController.getSingleRoute(routingInfo, (function() {
var testVal = Math.random()
return function(result,resultStatus){
console.log(testVal) // still always the same value
}())
I feel like I am missing something simple here but can't figure out what is happening.
I also feel I should note that I have also changed the code to use multiple instances of the GetSingleRouteHelper class instead of one and set all the values to class variables so setting this.testVal = Math.random() in each instance. Still, upon returning from the callbakcs the value of each class still results in the exact same value.
NOTE: Math.random() is just something I threw in there to try and test my values. It has no bearing on the actual info I am trying to pass (which are latitude and longitude values). So, I can't just move Math.random() inside the callback.
MapsController.getMultipleRoute(requests, function(res, status){
// this would mean it has failed to get the route via multiple waypoints
if (!status){
// break up each request into individual requests
for(var j = 0; j < results; j++){
GetSingleRouteHelper.singleCarRoute(singleRouteInfo)
}
}
}
GetSingleRouteHelper.singleCarRoute = function(routingInfo){
// what I have switched to to test. I need several atomic values that are generated in this scope.
var testVal = Math.random();
(function(testVal2){
console.log(testVal2) // here the value is as expected, is each individual random number that corresponds to the outer scopes "testVal" value
DatabaseController.getSingleRoute(routingInfo, function(result,
resultStatus) {
console.log(testVal2); // is always the final value that was declared. So if above function is run 10 times it will always be the 10th random number here.
}
}(testVal))
DatabaseController.getSingleRoute(data, callback){
// verifies some values are expected before sending forward
...
GetSingleRoute.get(data, function(a,b){
// verify integrity of return values
...
callback(a,b)
});
}
GetSingleRoute.get= function (data, callback) {
$.ajax({
type: "GET",
ws: "fetch_location",
dataType: "json",
data: data,
success: function success(res) {
callback(result, result.status)
},
}
I expect that in the callback of the DatabaseController.getSingleRoute function that the value of the testVal to be the correct random number for the scope of the closure. I am getting the final value. So if the function was run twice and the first random num was .3254 and the second random num was .2345 then when the DatabaseController.getSingleRoute callback is activated the console.log statement logs
.2345
.2345
instead of
.3252
.2345
I have the following code that I am trying to use to register a callback on an array of buttons. But I cannot seem to understand how I can bind the strings that I would need in the callback. Any suggestions would be much appreciated!
for (var i = 0; i < this.car_types.length; ++i) {
this.select_car_buttons.push($("#button_select_car_" +
this.car_types[i].car_type));
this.select_car_buttons[this.select_car_buttons.length - 1]
.click(function() {
console.log(this.car_types[i].car_type);
}.bind(this));
}
Somehow the this object is the button itself and not the object under whose scope the function is called.
EDIT : It seems like the this object was indeed being passed in properly. The issue is that the variable i is not going out of scope and is being captured by reference not by value. How should I go about solving this problem?
Also there seem to lots of such issues with JavaScript as a language (at least things that can be classified as an issue considering the semantics employed by the traditional C family languages such as C and C++ to be correct), is there some article I can read that warns me against these types of issues?
ANOTHER EDIT : On trying making a closure with the value of i captured by value I tried the following code
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) {
return function() {
console.log(this.car_types[scoped_i].car_type);
}.bind(this);
}(i)));
But I get the following error in Safari
TypeError: undefined is not an object (evaluating 'scoped_i')
EDIT : The same code works in Firefox and Chrome but not in Safari!
This is a scope issue. For modern browsers (that support ES6) you could just change var to let in your for loop and it would get fixed.
for (let i = 0; i < this.car_types.length; ++i)
Quoting the MDN docs
The let statement declares a block scope local variable, optionally initializing it to a value.
For more global support (non ES6 support) use an immediately invoked function to create extra scope for the variable (which you will pass as a parameter)
this.select_car_buttons[this.select_car_buttons.length - 1]
.click((function(scoped_i) { // IIF starts here, the new variable is called scoped_i for verbosity
return function() { // your original function code goes here
console.log(this.car_types[scoped_i].car_type); // use the newly scoped variable
}.bind(this);
}.bind(this)(i))); // end and execute the IIF while passing the i variable to it
Yes, this structure do make a lot of closures and make code very hard to read. Since you use jQuery, there are a much better way to solve this problem which saves the data in html:
html:
<button class="select-car" data-car-type="CarA">Select CarA</button>
<button class="select-car" data-car-type="CarB">Select CarB</button>
<!-- And a lot of buttons -->
js:
var selectCarOnClick = function() {
console.info($(this).data('car-type'));
};
$('button.select-car').click(selectCarOnClick);
Live exmaple: http://codepen.io/SCLeo/pen/VaQYjW
If you have a lot of other information to store and you want to use a object to store them instead of DOM, you can save car-name or car-id instead of car-type.
Here is the document about $.data: https://api.jquery.com/jquery.data/
var f_drum_min = function myself(a){
alert(a);
$f_min_node.push(a);
for (i=0;i<=$m;i++){
if ($f_leg[i][1]==a){
myself($f_leg[i][0]);
}
}
};
myself($f_leg[i][0]); breaks the for loop , how can I make it run multiple times in loop?
Your function is riddled with bad habits
There's no way for me to improve this function because I have no idea what all of those external states do. Nor is it immediately apparent what their data types are.
These are bad habits because there's no possible way to know the effect of your function. Its only input is a, yet the function depends on $f_min_node, $f_leg, and $m.
What is the value of those variables at the time you call your function?
What other functions change those values?
I assigned $f_min_node to some value and then called f_drum_min. How was I supposed to know that $f_min_node was going to get changed?
Every time you call your function, it's a big surprise what happens as a result. These are the woes of writing non-deterministic ("impure") functions.
Until you can fix these problems, recursion in a for-loop the least of your concerns
I have annotated your code with comments here
// bad function naming. what??
var f_drum_min = function myself(a){
// side effect
alert(a);
// external state: $f_min_node
// mutation: $f_min_node
$f_min_node.push(a);
// leaked global: i
// external state: $m
for (i=0;i<=$m;i++){
// external state: $f_leg
// loose equality operator: ==
if ($f_leg[i][1]==a){
myself($f_leg[i][0]);
}
}
};
I can help you write a deterministic recursive function that uses a linear iterative process though. Most importantly, it doesn't depend on any external state and it never mutates an input.
// Number -> Number
var fibonacci = function(n) {
function iter(i, a, b) {
if (i === 0)
return a;
else
return iter(i-1, b, a+b);
}
return iter(n, 0, 1);
}
fibonacci(6); // 8
for loops are pretty primitive; Imperative programmers will reach for it almost immediately thinking it's the only way to solve an iteration problem.
I could've used a for loop in this function, but thinking about the problem in a different way allows me to express it differently and avoid the for loop altogether.
One basic problem with the code, which would cause it to break under almost any circumstances, is that the loop variable i is a global, and is thus shared by all recursive invocations of the function.
For example, let's say the function is invoked for the first time. i is 0. Now it recurses, and let's say that the condition in the if is never true. At the end of the 2nd call, i = $m + 1. When you return to the first call, because i is global, the loop in the first call ends. I assume this is not what you want.
The fix for this is to declare i as local:
for (var i=0;i<=$m;i++){
This may or may not fix all of your problems (as pointed out in comments, we'd have to see more of your code to identify all possible issues), but it is a critical first step.