2

Edited: The //Fix ... and //Error comments in the code below show the answer

It's probably something stupid and obvious to JavaScript experts ... but I'll be danged if I can see what's up with this.

Here's my t.js:

var angularApp = angular.module('myApp', []);

angularApp.controller('mainController', ['$scope', '$http', '$log',
                                function ($scope,   $http,   $log) {
    $scope.version = angular.version;
    var ctime = new Date();
    $scope.ctime = ctime.getTime() / 1000;
    $http.get('http://127.0.0.1:8765/').then(
      function(res){
        $scope.stime = res['data']['server-time'] / 1000;
        // Fix goes here: $scope.skew = $scope.stime - $scope.ctime;
        },
      function(error){
        $log.warn('Unable to get time');});
    // Error here: this happens asynchronously to the $http.get!
    $scope.skew = parseFloat($scope.stime) - parseFloat($scope.ctime);
}]);

... and my HTML:

<!DOCTYPE html>
<html lang="en-us" data-ng-app="myApp"><head><title>Time Skew</title>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
  </head><body>
        <header><nav class="navbar navbar-default">
            <div class="container">
                <div class="navbar-header">
                    <a class="navbar-brand" href="/">AngularJS</a></div>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#"><i class="fa fa-home"></i>Home</a></li>
                </ul></div></nav></header>
        <div class="container">
            <div data-ng-controller="mainController">
                <div class="ng-cloak">Browser time: {{ctime}}</div>
                <div class="ng-cloak">Server &nbsp;&nbsp; time: {{stime}}</div>
                <div class="ng-cloak">Time skew: {{skew}}</div></div></div>
       <script src="//code.angularjs.org/1.4.7/angular.min.js"></script>
       <script src="t.js"></script>
    </body></html>

... and here's, roughly what's get displayed:

Browser time: 1445735550.067
Server  time: 1445735550.085
Time skew: NaN

Nothing is displayed on the Chrome browser's Developer Tools JavaScript Console as I run this. (I have seen plenty of other activity there for various other sorts of error during this study session ... so I know that it does show my syntax errors, failed dependency injections and so on --- when those are present).

I've tried this with and without the parseFloat(). (Also the calls for class="ng-cloak" are irelevant ... taking them out has no effect though they aren't working as intended either). The fact that I get the desired floating point representation from my * / 1000* (divisions) expressions suggests that they are treated as numbers at those two points in the code. I've tried removing the division and using parseInt() instead of parseFloat().

(Although I don't think it's relevant to this question here's the node.js "application" providing this time service:

// tserver.js:
var http = require("http");
http.createServer(function (req, response) {
    var headers = {
        'Content-Type:': 'application/json',
        // Extra headers suggested by http://stackoverflow.com/a/29548846/149076
        // ... to resolve: "Control-Allow-Origin' header is present on the requested resource"
        // error from Angular.js $http.get() ...
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        'Access-Control-Allow-Origin': '*'
        };
    response.writeHead(200, headers);
    var d = new Date();
    response.end(JSON.stringify({ 'server-time': d.getTime() }));
}).listen("8765");

... though it seems to be obvious that I am getting a valid integer (or string which should parse into an integer) from my "service." (The values displayed in the HTML are consistent to those for the "ctime" within a few milliseconds).

As I was posting this question I found a number of other questions about JavaScript NaN results ... and none of those seems to apply here. I even tried adding a $scope.tst = parseFloat('12345.97') - parseFloat('12345.67'); and added that to my HTML and got a perfectly predictable numeric result ... and I actually used these full length numbers, as displayed here, to ensure that it wasn't some weird issue with the size of the numbers. I also tried calling ".toString()" on the $scope.Xtime variables.

So what am I getting wrong here?

Jim Dennis
  • 17,054
  • 13
  • 68
  • 116

1 Answers1

5

$http is asynchronous and you are trying to use parseFloat on a variable that doesn't exist until the request completes

Put your calculation in the promise callback

$http.get('http://127.0.0.1:8765/').then(function (res) {
    $scope.stime = res['data']['server-time'] / 1000;
    // can do calc here
    $scope.skew = parseFloat($scope.stime) - parseFloat($scope.ctime);
},function (error) {
    $log.warn('Unable to get time');
});
charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • Good answer, beat me to it by a few seconds. – rockerest Oct 25 '15 at 01:46
  • 1
    Doh! Bit by the event model. Must.Remember.JavaScript.is.Implicitly.Multi-threaded! – Jim Dennis Oct 25 '15 at 01:47
  • 1
    @JimDennis no it's actually single threaded but some operations are asynchronous – charlietfl Oct 25 '15 at 01:48
  • Okay. Must.Remember.JavaScript.is.Executed.in.Implicitly.Asynchronous.Event.Loop! – Jim Dennis Oct 25 '15 at 05:43
  • Incidentally the calls to parseFloat() are superfluous in this case (as shown in my edits). I guess should use some sort of binding or watch ... or set my callback function to replace text in the DOM rather than using the {{...}} template reference or something. This fix works, but I still get that flicker that ng-cloak is supposed to fix. :( – Jim Dennis Oct 25 '15 at 05:55
  • did you include ng-cloak css ? Also feel free to accept answer if it helped – charlietfl Oct 25 '15 at 10:37