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.
Related
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.!
I'm using PouchDB which is async and i'm new to async..
So if i get a Doc from pouchdb i have to use .then to see the actual result. Thats clear for me, but do i have to call .then every single time i want this value?
For example i made a SettingsService where i can store my settings key / value style.
// Get Setting
settingsService.get = function(key) {
return settingDB.get(key).catch(function (err) {
console.log(err);
});
};
When i wanna get the setting do i have to make every time this big .then block?
// Get Time of Last Sync
settingsService.get('lastSync').then(function (setting) {
$scope.lastSync = setting.value; // now it's assigned isn't it?
console.log(setting);
});
//edit, but this is still is undefined.. so again use then?
console.log($scope.lastSync);
Now what if i want the same setting again little bit later in the code, do i everytime to repeat this .then stuff?
Yes, you would require then if you're trying to access the value anywhere outside of current then block.
// Get Time of Last Sync
settingsService.get('lastSync').then(function (setting) {
$scope.lastSync = setting.value;
console.log(setting);
// inside the then function, you can access the $scope.lastSync variable directly
});
Anything you want to do with respect to $scope.lastSync must be inside this function. Else you can even use $scope.$watch(lastSync, function(newValue){}) to process something outside the then function. This will make sure that it will run after the value has changed and it's again similar to then.
But outside the then, you must use then because you won't know when the value will resolve.
So console.log($scope.lastSync); anywhere outside the then will give you undefined. You can use $timeout to do console.log but you can't guarantee when the value will be available. It's totally depended on the service call.
Though you've assigned it inside the then, then would execute at some point of time when the service call succeeds. But you're printing the console.log for $scope.lastSync immediately. So it will be still be undefined because the then callback has not executed yet.
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.
See the following line of code:
WinJS.xhr({ url: "http://someurl.com" }).then(
function fulfilled(result)
{
if (result.status === 200)
{
resDiv.style.backgroundColor = "lightGreen";
resDiv.innerText = "Success";
}
});
As far as I understand, when WinJS.xhr has completed whatever it does then execute the anonymous function 'fulfilled' with argument 'result'
Coming from Java/C++ background, I'm extremely confused with how this code works - how is 'result' being passed to this function? Where does it say anything about what 'result' is? How can I know what type of object 'result' is and how it has a 'status' member?
I'm going to break my answer into two parts: The first concerns the actual execution model of Javascript, and the second which concerns the high-level expression as written.
The Javascript Execution Model
WinJS evaluates to an object.
That object has a prototype which contains an xhr member which WinJS.xhr evaluates to. That member is a function, which we will refer to as A below so that we can keep clear what exactly is going on.
Before we get to that, { url: "http://someurl.com" } returns an object which we will refer to as B.
That object B has a property called url.
A(B) calls a function A with a value B as an argument. It returns an object that we will refer to as C.
That object C has a prototype which contains a member named then. C.then happens to evaluate to a function. That function we will refer to as D.
function fulfilled(result) {...} returns a function that we will refer to as E. It can also be referred to as fulfilled but that fact is not used in this program fragment.
D(E) calls a function D with a value E as an argument. Nothing is done with the return value.
The high-level view
There are three functions here; one is a callback (called fulfilled), and the other two may be called "methods"- one xhr of the WinJS global object, and then of a promise object.
WinJS.xhr({ url: "http://someurl.com" }) creates and returns that promise object. You can convince yourself of this by consulting the documentation.
The promise object has a method called then which registers what you can think of as an event handler for when the promise is done. The value result - used in that callback registered in then comes from whatever is making that promise done by in fact calling the method done on that promise. You don't see the code that does that because it's someplace in the implementation of WinJS.xhr.
What WinJS.xhr is doing is performing a network request. When that network request is done it will signal the result of that network request (which according to the documentation is an XMLHttpRequest object) through the promise by calling the done() method on that promise. That in-turn calls the callback we registered with the then() method.
The documentation for the WinJS functions is still not great, IMO. You can look at the documentation for WinJS.xhr at http://msdn.microsoft.com/en-us/library/windows/apps/br229787.aspx, and that will give you some information on that - it says that the xhr function "wraps ... XMLHttpRequest object in a promise". Personally I've found it easier to look at the examples / quickstarts than at the reference documentations.
What you have passed to the fulfilled function (or the first function there) isn't the "result" of the operation, but the XMLHttpRequest object itself. On that you can get its properties to see the result - take a look at http://msdn.microsoft.com/en-us/library/windows/apps/ms535874.aspx for its reference.
The xhr method returns a Promise object, which has the then method that takes the onComplete, onError and onProcress callback functions.
This code only uses the onComplete callback. The callback is called when the request is completed, and it's sent a parameter with the value sent from the server.
If you are not familiar will callback functions, it may be clearer if you declare a regular function an use as the callback:
function fulfilled(result) {
if (result.status === 200) {
resDiv.style.backgroundColor = "lightGreen";
resDiv.innerText = "Success";
}
}
WinJS.xhr({ url: "http://someurl.com" }).then(fulfilled);
The then method is intended for pre-processing the result, you should rather use the done method to take care of the result.
I haven't found anything in the documentation that specifies exactly in what form the value comes from the server. It's probably there somewhere, but as usual with Microsoft documentation, it's rather complete but everything isn't everywhere so you have to look in different places to find specific information.
This only works in Chrome, not FF:
var menu = new Array();
$.couch.db("foo").allDocs({
success: function(d) {
for(var i=0;i<=d.total_rows-1;i++){
menu.push(d.rows[i].id);
};
}
});
console.log(menu);
I can console.log "menu" inside the function, but not outside. It's like it looses its scope. Any ideas?
Your success function gets called later, once you've received a response from $.couch.db, so when you reach console.log(menu); you still get the initial value.
Try declaring var menu = []; instead of new Array() - it shouldn't make any difference, but you never know.
Also, try putting console.log(menu); inside the loop, and see... oh, wait.
Forget the above, I just re-read the code.
I'm pretty sure allDocs() is an asynchronous function. You're trying to access the result after you started the function, but before it completed. All code that relies on menu MUST be in the success function.
Could it be that your function works asynchronous so, that when you console.log(menu) outside of the function it shows original state of the menu.
For example, in jQuery when you're using ajax function you can set parameter "async" to false so that response would be assigned to variable correctly.
The problem is that you are pushing data to the menu array inside a callback. What this means is that your code executes somehow like this:
new array is created
$.couch.db() is called which does something (some asynchronous task, which causes the execution flow to continue)
console.log() is called which usually prints a empty array since menu is empty
$.couch.db() finished doing it's business and has called your "success" callback which fills your array
But, there can be cases where your callback is executed before reaching the console.log() ... so really you are dealing with a race condition here.