Javascript Asynchronous Return Value - javascript

Just as I thought I understood JS scope...
Take this code:
function init() {
var page;
var pageName = $('body').data('page');
switch(pageName) {
// (there are more switch cases here normally...)
case 'pSearchLatest':
require(['searchresults'], function (SearchResults) {
page = new SearchResults().init();
console.log(page); // <- shows the object!
});
default:
break;
}
console.log(page); // <- undefined
return page;
}
See the console log comments. Why is the second returning undefined when the var page is declared outside of the scope of the switch statement?
EDIT
So I mistakenly thought this was a scope issue but it's down to the asynchronous nature of AMD.
How can I return the value of page in the require scope in the same method without a while loop checking for undefined?
EDIT 2
I did it like this:
function init(callback) {
case 'pSearchLatest':
require(['searchresults'], function (SearchResults) {
page = new SearchResults().init();
callback(page)
});
}
and in my page that calls the wrapping init() method:
new PageController().init(function(asyncViewController) {
App.view = asyncViewController;
});

You did understand scope correctly. The code in that inner function does indeed assign to the variable in the outer scope.
What you did not understand is asynchronous behaviour. The require function takes a callback function which will be invoked somewhen in the future - when the requested module is available. Yet, it does immediately return and control flow will lead to the console.log statement, which does print undefined - which is the value the page variable has at that time.
You should be able to recognise that since the undefined is logged first, and the log statement in the callback (with the object) executes later, even though it comes above in the code.

require is asynchronous, therefore your function did exit first and then processed callback function.

Two possible reasons... you didnt match the case. or the callback where the assignment of the value to page happens hasnt been executed yet. Im guessing its the latter since it would be readily apparent if you case was failing. Look into the documentation for whatever AMD library youre using and see how the execution of the function(s) passed to require are executed.
Also as a generaly rule try to avoid returning values determined by asynchronous calls like this. Instead use some kind of Observer pattern to subscribe to a particular event that can be triggered from different places.

Related

How is it possible for the Javascript interpreter to execute recursive functions?

lets take this example
function main (x) {
return x
}
function second() {
console.log("hello ")
}
js compiler knows all function declarations so I can call second inside main
main(second())
what I dont get about recursive functions is that how the same function can be called inside the function declaration
function factorial(n) {
if (n ===1) {
return 1;
} else {
return n * factorial(n-1);
}
}
my thought process is: okay this is the function declaration this is what the function does, but how can
can i call the same function even if the declaration is not finished
Because the function doesn't run when it is being defined. It only runs later, when it is called.
A function's code is the recipe for performing a computation. If at some point it calls another function it means the execution continues according to that recipe, and then when that is finished, the execution comes back to after the calling point, and resumes from there.
It doesn't matter whether you call the same function though. It just means that same recipe will be followed. But, crucially, each invocation is an independent copy of the recipe, each running independently in its own environment like call stack frame etc., which is what makes recursion possible.
See also these answers by me that talk about it some more, or that one.

Variable scope with jQuery and javascript

So, I wrote the following function:
function getData() {
var data;
$(function () {
$.getJSON('https://ipinfo.io', function (ipinfo) {
data = ipinfo;
console.log(data);
})
})
console.log(data);
}
The problem with the above is the 2nd console.log doesn't retain the info from the assignment inside the jQuery and logs an undefined object. I'm not exactly sure what is wrong, but I believe it to be something quite minor. However, as much as I've searched online, I haven't found an answer for this particular problem.
One line: Javascript is Asynchronous.
While many struggle to figure out what it exactly means, a simple example could possibly explain you that.
You request some data from a URL.
When the data from second URL is received, you wish to set a variable with the received data.
You wish to use this outside the request function's callback (after making the request).
For a conventional programmer, it is very hard to grasp that the order of execution in case of JavaScript will not be 1,2 and then 3 but rather 1,3,2.
Why this happens is because of Javascript's event-loop mechanism where each asynchronous action is tied with an event and callbacks are called only when the event occurs. Meanwhile, the code outside the callback function executes without holding on for the event to actually occur.
In your case:
var data;
$(function () {
$.getJSON('https://ipinfo.io', function (ipinfo) {//async function's callback
data = ipinfo;
console.log(data);//first console output
})
})
console.log(data);//second console output
While the async function's callback is executed when the data is received from the $.getJSON function, javascript proceeds further without waiting for the callback to assign the value to the data variable, causing you to log undefined in the console (which is the value of the data variable when you call console.log.
I hope I was able to explain that.!

IndexedDB - Can't pass variable after onsuccess when opening database

I have been looking for assistance with setting up an IndexedDB for web storage, and I have run into a problem that I cannot find a good answer to. After I have successfully setup/opened the database I am having trouble passing the variable that contains the database information to access it later. Here is my code, I have been following a guide from MDN :
const DB_NAME = 'database-name';
const DB_VERSION = 2;
const DB_STORE_NAME = 'users';
var db;
function openDb() {
console.log('openDb ...')
var request = indexedDB.open(DB_NAME, DB_VERSION);
request.onsuccess = function(event) {
db = request.result;
console.log("openDb DONE");
};
request.onerror = function(event) {
console.log(request.errorCode);
};
request.onupgradeneeded = function(event) {
console.log('openDb.onupgradeneeded');
var store = event.currentTarget.result.createObjectStore(DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
store.createIndex('age', 'age', { unique: false });
};
}
function getObjectStore(store_name, mode) {
var tx = db.transaction(store_name, mode);
return tx.objectStore(store_name);
}
When getObjectStore is called the variable db is undefined. My knowledge of javascript is very limited and some concepts I don't get. The guide doesn't not show anything special being done and their demo works as is. Some other guides have mentioned implementing a callback, but they don't show how its done nor do I understand the concept of callbacks. Any help is appreciated. Thanks.
Unfortunately you need to learn about a relatively complicated concept usually referred to as asynchronous JavaScript before proceeding to use indexedDB. There are already several thousand questions related to AJAX on stackoverflow. I am trying to think of the most polite way to say this, but basically, the answer you are looking for is already provided by these other questions, and by many other websites. Nevertheless, here are some quick tips.
First, your approach is never going to work. You cannot skip over learning about async.
Second, do not use the setTimeout trick to get it to work. That is horrible advice.
Third, at a general level, a callback is simply a word used to describe a function when the function is used in a particular way. Specifically, a callback refers to a function that is passed as an argument to another function, where the other function then maybe calls the function at some later point in time. More specifically, a callback is generally a function that is called at the end of the function it is passed to, when the function has completed.
For example:
function a(b) { alert(b); }
function c(d) { d('hi'); }
c(a);
That might look a bit confusing at first but it is the simplest thing I can describe off the top of my head. In the example, the final line calls function c and passes in function a. The effect of the code is that you see 'hi' as a browser alert. In this example, the function a is passed as a parameter/argument to function c. Function c uses the name d for its one and only argument. c calls d with the string 'hi'. When describing this example, we would say that argument d represents a callback function passed to function c. We could also say that function a is the particular callback function used by function c. So that is basically it. When you pass a function in as an argument and the other function calls the passed in argument, you are using a callback.
Then things gets way more complicated, because you have to learn about how to read and write asynchronous code. Properly introducing it would take several pages. Here is an extreme crash course.
You have traditionally been writing synchronous code, even if you did not call it that. Synchronous code runs exactly when you expect it to, in the order that you write your statements. Here is a brief example of typical sync code:
function sum(a, b) { return a + b; }
alert(sum(1, 2));
Simple stuff. The next example is code that uses a callback, but is still synchronous.
function doOperation(op, num1, num2) { return op(num1, num2); }
function sumOperation(num1, num2) { return num1 + num2; }
var result = doOperation(sumOperation, 1, 2);
alert(result);
Here we passed the sumOperation function into the doOperation function. sumOperation is the callback function. It is the first argument with the name 'op'. Still pretty simple stuff. Now consider the next example. The point of the next example is to show how we pass control to the function to do something. Kind of like how goto/labels work.
function doOperation(op, num1, num2) {
var result = op(num1, num2);
alert(result);
return undefined;
}
function sumOperation(num1, num2) { return num1 + num2; }
doOperation(sumOperation, 1, 2);
Notice how doOperation no longer returns a value. It has the logic within its function body. So once we call doOp, the browser starts running the code inside doOperation. So we switched from the outer context into the body of the function. Also, because doOperation doesn't return anything, we cannot do anything with its return value. The logic is locked inside the body of the doOperation function. The code still works about the same, its just that now we are not returning anything from doOperation, and now the logic is inside doOperation instead of outside in the main/global context.
Now an example that uses setTimeout. This is completely unrelated to the suggestion of using setTimeout.
function doOperation(op, num1, num2) {
setTimeout(function runLater() {
var result = op(num1, num2);
alert(result);
return undefined;
}, 1000);
return undefined;
}
function sumOperation(num1, num2) { return num1 + num2; }
doOperation(sumOperation, 1, 2);
The point here is to understand that we use a callback (named runLater in this example), and that the code inside the callback does not run immediately. Therefore, we can no longer say it runs synchronously. We instead refer to the statements constituting the body of the callback function as asynchronous. So now an alert appears after 1 second. Notice how we cannot return anything from runLater. Also notice how we cannot return anything from doOperation. There is nothing to return. There is no way to get the value in the 'result' variable out of the scope of runLater. It is locked in there.
Let's try almost the same thing, but try to have runLater set a variable. Also, I am going to omit 'return undefined' because that is what every function without a explicit return statement returns.
var aGlobalResult = null;
function doOperation(op, num1, num2) {
setTimeout(function runLater() {
aGlobalResult = op(num1, num2);
}, 1000);
}
function sumOperation(num1, num2) { return num1 + num2; }
doOperation(sumOperation, 1, 2);
alert(aGlobalResult);
Hopefully you are catching on to the problem. First off, runLater does not return anything, so doOperation does not return anything, so we could not even try to do something like aGlobalResult = doOperation(...);, because that would not make any sense. Second, the result here is that you will see an alert 'undefined' because the alert statement executes prior to the statement that assigns a value to aGlobalResult. This is even though you wrote the assignment statement higher up (earlier) in the code, and the alert is later. This is the brick wall some newer developers run into right here. This is indeed confusing for some. aGlobalResult is undefined here because setTimeout does not set it until later. Even if we passed in 0 milliseconds to setTimeout, it is still 'later', meaning that the assignment happens at a later point in time, after the alert. The alert message is always going to be undefined. There is absolutely nothing you can do to avoid the way this works. Nothing. Period. Stop trying. Learn it, or give up entirely.
So, how does one typically write code that behaves or involves asynchronous stuff? By using callbacks. Which again means that you can no longer use return statements to assign values to outer scope variables. You instead want to write functions and pass around control to various functions. In other words, instead of:
function a() {}
function b() {}
function c() {}
a(); b(); c();
you write code like this:
function a(callback) {
var asdf = 1+2; // do some stuff in a
alert('a finished');
// a has now completed, call its callback function, appropriately named callback
callback();
}
function b(callback) {
var asdfasdfasdf = 3 + 4;
alert('b finished');
// call the callback
callback();
}
a( function(){ b(function() { alert('both a and b finished'); }); });
This is more formally known as continuation passing style, or CPS.
So, that is an example of the very basics of writing callback functions and basic asynchronous code. Now you can start to use indexedDB. The first thing you will notice is that the function indexedDB.open is documented as asynchronous. So, how can we use it? Like this:
var someGlobalVariable = null;
var openRequest = indexedDB.open(...);
openRequest.onsuccess = function openRequestOnSuccessCallbackFunction(event) {
// Get a reference to the database variable. If this function is ever called at some later
// point in time, the database variable is now defined as the 'result' property of the
// open request. There are multiple ways to access the result property. Any of the following
// lines works 100% the same way. Use whatever you prefer. It does not matter.
var databaseConnection = openRequest.result;
var databaseConnection = this.result;
var databaseConnection = event.target.result;
var databaseConnection = event.currentTarget.result;
// Now that we have a valid and defined databaseConnection variable, do something with it
// e.g.:
var transaction = databaseConnection.transaction(...);
var objectStore = transaction.objectStore(...);
// etc.
// DO NOT DO THE FOLLOWING, it does not work. Why? Review the early descriptions. First off
// this onsuccess callback function does not return anything. Second off this function gets
// called at some *later* point in time, who knows when. It could be a nanosecond later.
someGlobalVariable = databaseConnection;
};
Hopefully that sets you on the path.
Edit: I thought I would add a bit more of an intro. A related concept you need to learn that I did not explain clearly enough regarding control is the difference between imperative and declarative programming.
Imperative programming involves executing a series of statements in the order you wrote. You are the caller and are in control. Imperative code looks like this (fictional code):
var dbConn = dbFactory.newConnection('mydb');
var tx = dbConn.newTransaction();
var resultCode = tx.put(myObject);
if(resultCode == dbResultConstants.ERROR_PUT_KEY_EXISTS) {
alert('uhoh');
}
Declarative programming is subtly different. With a declarative approach, you write functions, and then you register (aka hook or bind) the functions to the JavaScript engine, and then at some point later, when it is appropriate, the engine runs your code. The engine is the caller and is in control, not you. Declarative programming involves callbacks and looks like this (fictional code):
dbFactory.newConnection(function onConnect(dbConn) {
dbConn.newTransaction(function onNewTransaction(tx) {
tx.put(myObject, function onPut(resultCode) {
if(resultCode == dbResultConstants.ERROR_PUT_KEY_EXISTS) {
alert('uhoh');
}
});
});
});
In this example, the only thing you called was the fictional dbFactory.newConnection function. You passed in a callback function. You did not call the callback function yourself. The engine calls the callback function. You cannot call the callback function yourself. This is kind of the whole idea behind why JavaScript engines can allow you to write asynchronous code. Because you don't get to control the order of execution of statements/functions. The engine gets to control it. All you get to do is write your functions, register them, and then start a chain of callbacks (the sole imperative line, the starting statement).
So this is why a function like getObjectStore in your question will not work. You are trying to call the function yourself, but that is backwards. You can only write a function and register it (somehow hook it up as a callback somewhere) and then the engine, not you, calls it at some later point in time.
Hopefully this is not more confusing, but you could actually write your function getObjectStore if you really wanted to by passing in the database variable to the function as its first argument. This leads to the logical next question, how to get a valid database variable to pass into the function. You cannot get one in a global context (reliably). Because the connection variable is only valid within the context of the onOpen callback function. So you would have to make your call to this function from within the onOpen function. Something like:
function getObjectStore(db, name, mode) {
var tx = db.transaction(name, mode);
var store = tx.objectStore(name);
return store;
}
var openRequest = indexedDB.open(...);
openRequest.onsuccess = function onOpen(event) {
// get the connection variable. it is defined within this (onOpen) function and open.
var db = this.result;
// call our simple imperative helper function to get the users store. only call it from
// within this onOpen function because that is the only place we can get the 'db' variable.
var usersStore = getObjectStore(db, 'users', 'readwrite');
// do something here with usersStore, inside this function only.
};

confusion about node.js scoping

I'm writing a client for mpd (music player daemon) with node, using the node-mpdsocket library, but I appear to have run into a bit of confusion early on. In this example, response is an object, and response['state'] should return a string.
var togglePause = function() {
var mpdState;
mpd.send('status', function(response) {
mpdState = response.state;
// Here, console.log(mpdState) returns mpd's state correctly
console.log(mpdState);
});
// Here, undefined is returned regardless of mpd's actual state
console.log(mpdState);
}
I expected mpdState to return a string in both instances because both places where console.log are called are within the same function. However, this does not appear to be the case.
The callback passed to mpd.send is being invoked asynchronously. So, the second console.log statement is being called before the callback is ever run. The console.log statement inside the callback has the correct value as you can see.
The code you are running is behaving as expected.

Is this AJAX pattern a memory leak?

Consider this example of pretty standard method in Angular Js, which updates the view:
$scope.fetchResults = function() {
// Some local variable that will cause creation of closure
var hugeData = serviceX.getMilionRecords();
// Any call to any resource with success and error handlers.
$http({
method: "GET",
url: "/rest-api/bulk-operation-x",
params: { someParam: hugeData.length }
}).success( function () {
var length = hugeData.length;
$scope.reportToUser("Success, that was " + length + " records being processed!";
}).error( function () {
var length = hugeData.length;
$scope.reportToUser("Something went wrong while processing " + length + " records... :-(";
});
};
This is of course hypothetical example, but it nicely shows pattern, which could be described as reusing of local variables from within AJAX callbacks.
Of course in both handlers (success and error) we are creating a closure over hugeData which is directly referenced from callback handlers.
My question is: since the result of AJAX call can be only either success or failure, will reusing of this code cause the memory leak over time? I would answer "yes", but I couldn't reliably prove this one in my local tests.
I'd like some more experienced guru to explain this one for me. I'd love response from anyone working with Angular on daily basis, but any jquery responses are welcome as well.
You will have a memory leak as soon as you return the result of $http() call (or whatever object or function that has access to hugeData) into the outer scope of fetchResults.
With your code, nothing big is exposed directly outside of fetchResults, and the result of $http() call will live until it either succeeds or fail, then calling the corresponding callback, finally getting GC'ed.
See for insights: http://jibbering.com/faq/notes/closures/#clIdRes
As #ŁukaszBachman observes, this does not guarantee that there are no memory leaks. Any dangling reference to your big object or to your callback with big object in scope, will cause woe.
So, let's check into $q implementation ($http is based on $q).
If you check https://github.com/angular/angular.js/blob/master/src/ng/q.js#L191, you can see that the resolve() method of the deferred first copies the list of registered callbacks in a variable local to the method:
var callbacks = pending;
subsequently nullifies the external pending (that was defined at the defer level)
pending = undefined;
then, at next tick, executes the callbacks. Things may get complicated by the fact that the callback's argument may be a deferred itself (adding a further delay to execution), but at most you could get into an infinite loop. (And that's not funny!). If you are lucky enough not to get into the loop, then at some point the callback array is exhausted, and then there is no reference whatsoever to the callback list, so it's available for GC.
But.
Things may go wrong if you force them to.
You may use arguments.callee inside a callback.
You can throw beer on your keyboard too.
If you jump out of the window, unless you live on the first floor, you will probably get hurt.
Happy EcmaScripting!

Categories

Resources