-1

I would like to write a function that runs on the client side with JavaScript but checks the existence of a file on server side. I try using Ajax thus.

function ROIFileExists(){
    var fileAlreadyExists=undefined;

    jQuery.ajax({
        type: "POST",
        url: "ROIFileExists.php",
        data: { FileName: fileName},
        cache: false
        }).done(function( result ) {
            fileAlreadyExists = (result!==0);
            console.log('fileAlreadyExists='+fileAlreadyExists);
    }); 

    return fileAlreadyExists;
}

The problem is that, since Ajax is asynchronous, fileAlreadyExists is usually returned (as undefined) before it is set by the Ajax block.

Scott
  • 21,211
  • 8
  • 65
  • 72
OtagoHarbour
  • 3,969
  • 6
  • 43
  • 81

2 Answers2

5

Use a callback, or your own Promise.

Here's the callback approach:

function ROIFileExists(callback){
// --------------------^ Accept the function to call back as an arg

    jQuery.ajax({
        type: "POST",
        url: "ROIFileExists.php",
        data: { FileName: fileName},
        cache: false
        }).done(function( result ) {
            var fileAlreadyExists = (result!==0);
            console.log('fileAlreadyExists='+fileAlreadyExists);
            callback(fileAlreadyExists);
// ---------^ Call it
    }); 
}

Usage:

ROIFileExists(function(exists) {
    console.log("Exists? " + exists);
});

Here's the Promise approach:

function ROIFileExists(){
    var d = new $.Deferred();                   // Create the deferred object

    jQuery.ajax({
        type: "POST",
        url: "ROIFileExists.php",
        data: { FileName: fileName},
        cache: false
        }).done(function( result ) {
            var fileAlreadyExists = (result!==0);
            console.log('fileAlreadyExists='+fileAlreadyExists);
            d.resolve(fileAlreadyExists);       // <=== Resolve it
    }); 

    return d;                                   // <=== Return it
}

Usage:

ROIFileExists().done(function(exists) {
    console.log("Exists? " + exists);
});

I would strongly suggest not using async: false. It leads to a poor user experience, locking up the UI of the browser during the call; it's going to go away in a future version of jQuery (you'd have to use XMLHttpRequest yourself); and you can't use async: false if you ever have a similar need but when you're using JSONP.

JavaScript code on browsers is event-driven anyway, so embracing that event-driven nature (in this case, the event being that you get the information back about the file) is usually the way to go.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • +1 I didn't know about the `$.Deferred` object that's pretty cool. – Scott Jul 20 '13 at 15:44
  • 1
    @Scott: Yeah. At first glance, they only seem to add complication (at least, that was my first reaction). But then when you start combining them, waiting on more than one thing at once, etc., they start getting pretty cool. :-) – T.J. Crowder Jul 20 '13 at 15:46
  • 1
    The callback approach works great! Thanks very much! I like the look of the promise approach as well. Thanks again! – OtagoHarbour Jul 20 '13 at 15:51
2

You can use async: false to insure the result is returned by your function. (Note: async: false is not optimal, and should be avoided if you can change the architecture of your application to support callbacks, as per below.)

function ROIFileExists(){
    var fileAlreadyExists=undefined;

    jQuery.ajax({
        async: false,   // Add this line
        type: "POST",
        url: "ROIFileExists.php",
        data: { FileName: fileName},
        cache: false
        }).done(function( result ) {
            fileAlreadyExists = (result!==0);
            console.log('fileAlreadyExists='+fileAlreadyExists);
    }); 

    return fileAlreadyExists;
}

However it is best to use a callback function.

function ROIFileExists(callback){
    jQuery.ajax({
        type: "POST",
        url: "ROIFileExists.php",
        data: { FileName: fileName},
        cache: false
        }).done(function( result ) {
            callback(result!==0);
       });
}
Scott
  • 21,211
  • 8
  • 65
  • 72
  • 4
    `async: false` is a terrible fix for a problem that should be solved by changing the architecture of the code. – Pekka Jul 20 '13 at 15:27
  • @Pekka웃 I don't condone it's use, I would always avoid it personally. That being said some scenarios don't allow the changing of architecture, I have included the callback method too. – Scott Jul 20 '13 at 15:30
  • @Pekka. I had thought about different architecture like putting the code that responds to the result in the .done() block. But that would customize the code rather than making it generic. – OtagoHarbour Jul 20 '13 at 15:31