0

I have a function that goes to a PHP script which returns the Server Operating System.

The script is literally dead simple:

<?php
    echo (strpos(PHP_OS, 'Linux') > -1 ? 'Lin' : 'Win');

My goal is to be able to differentiate between operating systems so that I can declare a global path variable in my .js file for future uses.

This is what I've done so far:

function serverOS()
{
    var os;

    $.ajax({
        url: '../scripts/ajax/detect-os.php',
        type: 'get',
        success: function(res)
        {
            os = res;
            return os;
        },
        error: function(res) {alert('Major Error!'); console.log(res)}
    });

    return os;
}

console.log(serverOS());

The ending console.log outputs undefined - but if I console.log os inside of the success callback function, then it outputs what I expect.

According to this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var

I should be able to do what I want with the above script but it doesn't seem to work. How do I go about setting and getting a global variable using ajax in JavaScript/jQuery?

treyBake
  • 6,440
  • 6
  • 26
  • 57
  • 3
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Jeremy Thille Aug 21 '18 at 11:55
  • The one, only, eternal question about asynchronism :) – Jeremy Thille Aug 21 '18 at 11:55
  • 2
    why not define this variable in the html head, without the need of a server request, something like this – chebaby Aug 21 '18 at 11:58
  • @chebaby because you shouldn't mix PHP and JS ever ... – treyBake Aug 21 '18 at 11:59
  • PHP and JS _can't_ mix. One runs on the server, the other in the browser. No risk to mix them, go ahead. – Jeremy Thille Aug 21 '18 at 12:01
  • @ThisGuyHasTwoThumbs, says who? of course you can!! – chebaby Aug 21 '18 at 12:02
  • @chebaby it's a bad practice - as Jeremy already said - one runs on the server, the other in browser - it's better to keep them separate – treyBake Aug 21 '18 at 12:03
  • But what do you mean? I just said you _can't_ mix them up. They are physically separated, they run on different machines. Keeping them separated is not a "good practice", it's a technical constraint... – Jeremy Thille Aug 21 '18 at 12:05
  • @JeremyThille - true as it may be - you still can do `` and it still work - I've seen it and it makes my heart hurt - so I regard it more bad practice than a constraint – treyBake Aug 21 '18 at 12:06
  • What's inside `` is interpreted server-side and disappears. The result is then sent to the browser, where the JS code is then interpreted. This is why it's impossible to mix them. JS has no idea about PHP's existence. – Jeremy Thille Aug 21 '18 at 12:07
  • @JeremyThille I'm not arguing with your logic - honestly I'm on your side - I've just seen it happen and work - don't shoot the messenger :'( – treyBake Aug 21 '18 at 12:08
  • @ThisGuyHasTwoThumbs I agree it's better, but if this one simple line of code could solve the problem, i would personally use it (and i did in so many projects), never had a problem what so ever. – chebaby Aug 21 '18 at 12:10

2 Answers2

2

AJAX operations are asynchronous. They will not block the rest of your JavaScript from executing.

The final return statement in your function attempts to return os immediately (before the AJAX operation has completed. Remove that return statement and in the success handler take care of all the logic to get the value back to the caller.

function serverOS() {
  // The AJAX method will invoke the logging function no matter what.
  // But, it won't happen until the AJAX call is complete.
  $.ajax({
    url: '../scripts/ajax/detect-os.php',
    type: 'get',
    success: function(res) {
      returnValue(res);
    },
    error: function(res) {
      alert('Major Error!');
      returnValue(res);
    }
  });
}
 
function returnValue(val){
  console.log(val);
}

serverOS();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Yeah... except that `console.log` is synchronous, so it will never display the OS type. `serverOS()` doesn't return anything, so the console output will be `undefined`. This code won't work. – Jeremy Thille Aug 21 '18 at 11:57
  • Yes, because obviously there's no actual PHP file to fetch here. But it will work in your project – Jeremy Thille Aug 21 '18 at 12:09
  • @ThisGuyHasTwoThumbs That simply means that your AJAX call errored out. That isn't because of my code solution. It's because something about the way you're configuring your AJAX call isn't correct. – Scott Marcus Aug 21 '18 at 12:10
  • @ScottMarcus just a quick question - how do you assign the `val` in the `returnValue` function to a global variable for use throughout the script? – treyBake Aug 21 '18 at 12:15
  • @ThisGuyHasTwoThumbs In the success handler, you could assign a previously declared global (you had `os`) with the AJAX return value (`res`). The problem is that you have to know, at what point in the program it becomes OK to access that global. – Scott Marcus Aug 21 '18 at 12:18
  • @ScottMarcus thanks Scott :) I found an alternative (see my answer) - but yours will stay marked as yours is way forward :) – treyBake Aug 21 '18 at 12:30
0

Scott's Answer definitely works - but there does also seem to be an alternative I stumbled across. There's an AJAX property called async. Setting this to false in my function means it becomes a synchronous ajax call. Changing my function to this:

var os;

function serverOS()
{
    $.ajax({
        url: '../scripts/ajax/detect-os.php',
        type: 'get',
        async: false,
        success: function(res)
        {
            returnValue(res)
        },
        error: function(res)
        {
            alert('Major Error!');
            returnValue(res)
        }
    });
}

function returnValue(val)
{
    os = val;
    return os;
}

serverOS();

console.log(os); //this print Lin on my Linux machine.

ref: https://api.jquery.com/jQuery.ajax/

treyBake
  • 6,440
  • 6
  • 26
  • 57
  • No, actually this takes you right back to where you started. The `console.log()` will be executed immediately after the `serverOS()` invocation, before it has a chance to finish the AJAX call. You can't count on this technique. You can/should only access the return value from an AJAX call from within it's success handler or, at least with a `null` check. – Scott Marcus Aug 21 '18 at 12:37
  • Yes, that may be, but "it works for me" are the 4 worst words to ever say in web development. Your results may be cached, causing your AJAX call to return very quickly - quick enough so that the `os` variable has been populated by the time `console.log()` is encountered. But, the time it takes for an AJAX call to complete can't be know for each user. – Scott Marcus Aug 21 '18 at 12:45
  • @ScottMarcus tested in private window ... :S I get where you're coming from but it does work in private window (cache disabled) – treyBake Aug 21 '18 at 12:46
  • Again, 'it works" isn't the right criteria to go by with AJAX. You may be working against your own local server, therefore bypassing any network latency/bottlenecks that others will encounter. You simply can't just access the result of an AJAX call, until you know for sure the call has completed. This is the entire point of the `success` callback. – Scott Marcus Aug 21 '18 at 12:50
  • @ScottMarcus but if it's synchronous surely it just runs line-by-line in the file rather than whilst other things are happening? – treyBake Aug 21 '18 at 12:51
  • Ah, well, I honestly didn't see that you are making this a synchronous call. And, in that case, the code you have is fine. But, in real practice, this is not a good idea because now the UI is blocked until the AJAX call has returned and, as I said, you have no idea how long that is going to take for other users (they have different network connections than you do, you may not be in full control of the speed of the server, etc.). – Scott Marcus Aug 21 '18 at 12:53