0

Currently, I have a function getData that makes an AJAX call for some data, stores it in localStorage, and then calls the callback. If the data is already in localStorage, then getData will just call the callback.

// A wrapper around localStorage.
var storage = new Storage();

function getData(callback){
    var KEY = 'key';
    var data = storage.get(KEY);

    if (data){
        callback(data);
    } else {
        Utils.ajaxPost({
            ...,
            success: function(data){
                storage.set(KEY, data);
                callback(data);
            }
        });
    }
}

// I use getData like this.
getData(function(data){
    // do something with data
});

I am wondering if there is a way I can write the getData function (perhaps with deferred/promises/closures) such that I can use it like this:

var data = getData();
// do something with data
gruuuvy
  • 2,028
  • 4
  • 31
  • 52

1 Answers1

1

Due to the asynchronous nature of AJAX, it is not possible to write:

function getData(){
  var result;
  $.ajax({
    url: 'script.php',
    type: 'POST',
    dataType: 'html',
    cache: false,
    succes: function(data){
      result = data;
    }
  });
  return result;
}

var data = getData();
console.log(data);  //undefined

This will result in data being undefined as the function will return before the request to the server completes.

You can get around this by setting async: false, but this is generally regarded as a bad idea, as it freezes the browser until it receives a response.

function getData(){
  var result;
  $.ajax({
    url: 'submit.php',
    type: 'POST',
    dataType: 'html',
    async: false,
    success: function(data){
      result = data;
    }
  });
  return result;
}

var data = getData();
console.log(data); // Server's response

A better idea is to use callbacks (which you seem to already be doing)

getData(function(data){
    // do something with data
});

Or promises:

function getData(){
  return $.ajax({
    url: 'submit.php',
    type: 'POST',
    dataType: 'html'
  });
}

var data = getData()
.success(function(data){
  console.log(data);
})
.fail(function(){
  console.log("Error!");
});

Promises are a good solution for dealing with multiple callbacks and decoupling your code.

To make that work in your case, you'd have to alter your code somewhat to have the first part of the if statement also return a promise:

function getData(key){
  var data = localStorage.getItem(key);

  if (data){
    return $.Deferred().resolve(data);
  } else {
    return $.ajax({
      url: 'submit.php',
      type: 'POST',
      dataType: 'html',
      cache: false
    });
  }
}

var data = getData("key").done(function(data){
  localStorage.setItem("key", data);
  console.log(data)
});

I'm not sure how much sense it makes to do it this way, though.

Summary: you can do what you are asking, but only using async: false, which is usually considered a bad idea. Here's how:

function getData(){
  var KEY = 'key',
      data = localStorage.getItem(KEY),
      retVal;

  if (data){
    retVal = data;
  } else {
    $.ajax({
      url: 'submit.php',
      type: 'POST',
      dataType: 'html',
      async: false,
      success: function(data){
        retVal = data;
        localStorage.setItem(KEY, data);
      }
    });
  }

  return retVal;
}

var data = getData();
console.log(data);

Very useful reference: How do I return the response from an asynchronous call?

Community
  • 1
  • 1
James Hibbard
  • 16,490
  • 14
  • 62
  • 74
  • Thank you. Using `async: false` was not what I had in mind, but the promises approach you outlined is something I will play around with and consider! – gruuuvy Dec 18 '14 at 18:00