Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
113 views
in Technique[技术] by (71.8m points)

javascript - jQuery: Return data after ajax call success


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Note: This answer was written in February 2010.
See updates from 2015, 2016 and 2017 at the bottom.

You can't return anything from a function that is asynchronous. What you can return is a promise. I explained how promises work in jQuery in my answers to those questions:

If you could explain why do you want to return the data and what do you want to do with it later, then I might be able to give you a more specific answer how to do it.

Generally, instead of:

function testAjax() {
  $.ajax({
    url: "getvalue.php",  
    success: function(data) {
      return data; 
    }
  });
}

you can write your testAjax function like this:

function testAjax() {
  return $.ajax({
      url: "getvalue.php"
  });
}

Then you can get your promise like this:

var promise = testAjax();

You can store your promise, you can pass it around, you can use it as an argument in function calls and you can return it from functions, but when you finally want to use your data that is returned by the AJAX call, you have to do it like this:

promise.success(function (data) {
  alert(data);
});

(See updates below for simplified syntax.)

If your data is available at this point then this function will be invoked immediately. If it isn't then it will be invoked as soon as the data is available.

The whole point of doing all of this is that your data is not available immediately after the call to $.ajax because it is asynchronous. Promises is a nice abstraction for functions to say: I can't return you the data because I don't have it yet and I don't want to block and make you wait so here's a promise instead and you'll be able to use it later, or to just give it to someone else and be done with it.

See this DEMO.

UPDATE (2015)

Currently (as of March, 2015) jQuery Promises are not compatible with the Promises/A+ specification which means that they may not cooperate very well with other Promises/A+ conformant implementations.

However jQuery Promises in the upcoming version 3.x will be compatible with the Promises/A+ specification (thanks to Benjamin Gruenbaum for pointing it out). Currently (as of May, 2015) the stable versions of jQuery are 1.x and 2.x.

What I explained above (in March, 2011) is a way to use jQuery Deferred Objects to do something asynchronously that in synchronous code would be achieved by returning a value.

But a synchronous function call can do two things - it can either return a value (if it can) or throw an exception (if it can't return a value). Promises/A+ addresses both of those use cases in a way that is pretty much as powerful as exception handling in synchronous code. The jQuery version handles the equivalent of returning a value just fine but the equivalent of complex exception handling is somewhat problematic.

In particular, the whole point of exception handling in synchronous code is not just giving up with a nice message, but trying to fix the problem and continue the execution, or possibly rethrowing the same or a different exception for some other parts of the program to handle. In synchronous code you have a call stack. In asynchronous call you don't and advanced exception handling inside of your promises as required by the Promises/A+ specification can really help you write code that will handle errors and exceptions in a meaningful way even for complex use cases.

For differences between jQuery and other implementations, and how to convert jQuery promises to Promises/A+ compliant, see Coming from jQuery by Kris Kowal et al. on the Q library wiki and Promises arrive in JavaScript by Jake Archibald on HTML5 Rocks.

How to return a real promise

The function from my example above:

function testAjax() {
  return $.ajax({
      url: "getvalue.php"
  });
}

returns a jqXHR object, which is a jQuery Deferred Object.

To make it return a real promise, you can change it to - using the method from the Q wiki:

function testAjax() {
  return Q($.ajax({
      url: "getvalue.php"
  }));
}

or, using the method from the HTML5 Rocks article:

function testAjax() {
  return Promise.resolve($.ajax({
      url: "getvalue.php"
  }));
}

This Promise.resolve($.ajax(...)) is also what is explained in the promise module documentation and it should work with ES6 Promise.resolve().

To use the ES6 Promises today you can use es6-promise module's polyfill() by Jake Archibald.

To see where you can use the ES6 Promises without the polyfill, see: Can I use: Promises.

For more info see:

Future of jQuery

Future versions of jQuery (starting from 3.x - current stable versions as of May 2015 are 1.x and 2.x) will be compatible with the Promises/A+ specification (thanks to Benjamin Gruenbaum for pointing it out in the comments). "Two changes that we've already decided upon are Promise/A+ compatibility for our Deferred implementation [...]" (jQuery 3.0 and the future of Web development). For more info see: jQuery 3.0: The Next Generations by Dave Methvin and jQuery 3.0: More interoperability, less Internet Explorer by Paul Krill.

Interesting talks

UPDATE (2016)

There is a new syntax in ECMA-262, 6th Edition, Section 14.2 called arrow functions that may be used to further simplify the examples above.

Using the jQuery API, instead of:

promise.success(function (data) {
  alert(data);
});

you can write:

promise.success(data => alert(data));

or using the Promises/A+ API:

promise.then(data => alert(data));

Remember to always use rejection handlers either with:

promise.then(data => alert(data), error => alert(error));

or with:

promise.then(data => alert(data)).catch(error => alert(error));

See this answer to see why you should always use rejection handlers with promises:

Of course in this example you could use just promise.then(alert) because you're just calling alert with the same arguments as your callback, but the arrow syntax is more general and lets you write things like:

promise.then(data => alert("x is " + data.x));

Not every browser supports this syntax yet, but there are certain cases when you're sure what browser your code will run on - e.g. when writing a Chrome extension, a Firefox Add-on, or a desktop application using Electron, NW.js or AppJS (see this answer for details).

For the support of arrow functions, see:

UPDATE (2017)

There is an even newer syntax right now called async functions with a new await keyword that instead of this code:

functionReturningPromise()
    .then(data => console.log('Data:', data))
    .catch(error => console.log('Error:', error));

lets you write:

try {
    let data = await functionReturningPromise();
    console.log('Data:', data);
} catch (error) {
    console.l

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...