I have a function foo
which makes an Ajax request. How can I return the response from foo
?
I tried returning the value from the success
callback as well as assigning the response to a local variable inside the function and returning that one, but none of those ways actually return the response.
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response; // <- I tried that one as well
}
});
return result;
}
var result = foo(); // It always ends up being `undefined`.
Use a
callback()
function inside thefoo()
success. Try in this way. It is simple and easy to understand.Of course there are many approaches like synchronous request, promise, but from my experience I think you should use the callback approach. It's natural to asynchronous behavior of Javascript. So, your code snippet can be rewrite a little different:
The problem
The A in Ajax stands for asynchronous . That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example,
$.ajax
returns immediately and the next statement,return result;
, is executed before the function you passed assuccess
callback was even called.Here is an analogy which hopefully makes the difference between synchronous and asynchronous flow clearer:
Synchronous
Imagine you make a phone call to a friend and ask him to look something up for you. Although it might take a while, you wait on the phone and stare into space, until your friend gives you the answer that you needed.
The same is happening when you make a function call containing "normal" code:
Even though
findItem
might take a long time to execute, any code coming aftervar item = findItem();
has to wait until the function returns the result.Asynchronous
You call your friend again for the same reason. But this time you tell him that you are in a hurry and he should call you back on your mobile phone. You hang up, leave the house and do whatever you planned to do. Once your friend calls you back, you are dealing with the information he gave to you.
That's exactly what's happening when you do an Ajax request.
Instead of waiting for the response, the execution continues immediately and the statement after the Ajax call is executed. To get the response eventually, you provide a function to be called once the response was received, a callback (notice something? call back ?). Any statement coming after that call is executed before the callback is called.
Solution(s)
Embrace the asynchronous nature of JavaScript! While certain asynchronous operations provide synchronous counterparts (so does "Ajax"), it's generally discouraged to use them, especially in a browser context.
Why is it bad do you ask?
JavaScript runs in the UI thread of the browser and any long-running process will lock the UI, making it unresponsive. Additionally, there is an upper limit on the execution time for JavaScript and the browser will ask the user whether to continue the execution or not.
All of this is really bad user experience. The user won't be able to tell whether everything is working fine or not. Furthermore, the effect will be worse for users with a slow connection.
In the following we will look at three different solutions that are all building on top of each other:
async/await
(ES2017+, available in older browsers if you use a transpiler or regenerator)then()
(ES2015+, available in older browsers if you use one of the many promise libraries)All three are available in current browsers, and node 7+.
ES2017+: Promises with
async/await
The ECMAScript version released in 2017 introduced syntax-level support for asynchronous functions. With the help of
async
andawait
, you can write asynchronous in a "synchronous style". The code is still asynchronous, but it's easier to read/understand.async/await
builds on top of promises: anasync
function always returns a promise.await
"unwraps" a promise and either result in the value the promise was resolved with or throws an error if the promise was rejected.Important: You can only use
await
inside anasync
function. Right now, top-levelawait
isn't yet supported, so you might have to make an async IIFE (Immediately Invoked Function Expression) to start anasync
context.You can read more about
async
andawait
on MDN.Here is an example that builds on top of delay above:
Current browser and node versions support
async/await
. You can also support older environments by transforming your code to ES5 with the help of regenerator (or tools that use regenerator, such as Babel).Let functions accept callbacks
A callback is simply a function passed to another function. That other function can call the function passed whenever it is ready. In the context of an asynchronous process, the callback will be called whenever the asynchronous process is done. Usually, the result is passed to the callback.
In the example of the question, you can make
foo
accept a callback and use it assuccess
callback. So thisbecomes
Here we defined the function "inline" but you can pass any function reference:
foo
itself is defined as follows:callback
will refer to the function we pass tofoo
when we call it and we simply pass it on tosuccess
. I.e. once the Ajax request is successful,$.ajax
will callcallback
and pass the response to the callback (which can be referred to withresult
, since this is how we defined the callback).You can also process the response before passing it to the callback:
It's easier to write code using callbacks than it may seem. After all, JavaScript in the browser is heavily event driven (DOM events). Receiving the Ajax response is nothing else but an event.
Difficulties could arise when you have to work with third-party code, but most problems can be solved by just thinking through the application flow.
ES2015+: Promises with then()
The Promise API is a new feature of ECMAScript 6 (ES2015), but it has good browser support already. There are also many libraries which implement the standard Promises API and provide additional methods to ease the use and composition of asynchronous functions (e.g. bluebird).
Promises are containers for future values. When the promise receives the value (it is resolved) or when it is canceled (rejected), it notifies all of its "listeners" who want to access this value.
The advantage over plain callbacks is that they allow you to decouple your code and they are easier to compose.
Here is a simple example of using a promise:
Applied to our Ajax call we could use promises like this:
Describing all the advantages that promise offer is beyond the scope of this answer, but if you write new code, you should seriously consider them. They provide a great abstraction and separation of your code.
More information about promises: HTML5 rocks - JavaScript Promises
Side note: jQuery's deferred objects
Deferred objects are jQuery's custom implementation of promises (before the Promise API was standardized). They behave almost like promises but expose a slightly different API.
Every Ajax method of jQuery already returns a "deferred object" (actually a promise of a deferred object) which you can just return from your function:
Side note: Promise gotchas
Keep in mind that promises and deferred objects are just containers for a future value, they are not the value itself. For example, suppose you had the following:
This code misunderstands the above asynchrony issues. Specifically,
$.ajax()
doesn't freeze the code while it checks the '/password' page on your server - it sends a request to the server and while it waits, it immediately returns a jQuery Ajax Deferred object, not the response from the server. That means theif
statement is going to always get this Deferred object, treat it astrue
, and proceed as though the user is logged in. Not good.But the fix is easy:
Not recommended: Synchronous "Ajax" calls
As I mentioned, some(!) asynchronous operations have synchronous counterparts. I don't advocate their use, but for completeness' sake, here is how you would perform a synchronous call:
Without jQuery
If you directly use a
XMLHTTPRequest
object, passfalse
as third argument to.open
.jQuery
If you use jQuery, you can set the
async
option tofalse
. Note that this option is deprecated since jQuery 1.8. You can then either still use asuccess
callback or access theresponseText
property of the jqXHR object:If you use any other jQuery Ajax method, such as
$.get
,$.getJSON
, etc., you have to change it to$.ajax
(since you can only pass configuration parameters to$.ajax
).Heads up! It is not possible to make a synchronous JSONP request. JSONP by its very nature is always asynchronous (one more reason to not even consider this option).
Angular1
For people who are using AngularJS, can handle this situation using
Promises
.Here it says,
You can find a nice explanation here also.
Example found in docs mentioned below.
Angular2 and Later
In
Angular2
with look at the following example, but its recommended to useObservables
withAngular2
.}
You can consume that in this way,
See the original post here. But Typescript does not support native es6 Promises, if you want to use it, you might need plugin for that.
Additionally here is the promises spec define here.
Another solution is to execute code via sequential executor nsynjs.
If underlying function is promisified
nsynjs will evaluate all promises sequentially, and put promise result into
data
property:If underlying function is not promisified
Step 1. Wrap function with callback into nsynjs-aware wrapper (if it has promisified version, you can skip this step):
Step 2. Put synchronous logic into function:
Step 3. Run function in synchronous manner via nsynjs:
Nsynjs will evaluate all operators and expressions step-by-step, pausing execution in case if result of some slow function is not ready.
More examples here: https://github.com/amaksr/nsynjs/tree/master/examples
Most of the answers here give useful suggestions for when you have a single async operation, but sometimes, this comes up when you need to do an asynchronous operation for each entry in an array or other list-like structure. The temptation is to do this:
Example:
The reason that doesn't work is that the callbacks from
doSomethingAsync
haven't run yet by the time you're trying to use the results.So, if you have an array (or list of some kind) and want to do async operations for each entry, you have two options: Do the operations in parallel (overlapping), or in series (one after another in sequence).
Parallel
You can start all of them and keep track of how many callbacks you're expecting, and then use the results when you've gotten that many callbacks:
Example:
(We could do away with
expecting
and just useresults.length === theArray.length
, but that leaves us open to the possibility thattheArray
is changed while the calls are outstanding...)Notice how we use the
index
fromforEach
to save the result inresults
in the same position as the entry it relates to, even if the results arrive out of order (since async calls don't necessarily complete in the order in which they were started).But what if you need to return those results from a function? As the other answers have pointed out, you can't; you have to have your function accept and call a callback (or return a Promise). Here's a callback version:
Example:
Or here's a version returning a
Promise
instead:Of course, if
doSomethingAsync
passed us errors, we'd usereject
to reject the promise when we got an error.)Example:
(Or alternately, you could make a wrapper for
doSomethingAsync
that returns a promise, and then do the below...)If
doSomethingAsync
gives you a Promise, you can usePromise.all
:If you know that
doSomethingAsync
will ignore a second and third argument, you can just pass it directly tomap
(map
calls its callback with three arguments, but most people only use the first most of the time):Example:
Note that
Promise.all
resolves its promise with an array of the results of all of the promises you give it when they are all resolved, or rejects its promise when the first of the promises you give it rejects.Series
Suppose you don't want the operations to be in parallel? If you want to run them one after another, you need to wait for each operation to complete before you start the next. Here's an example of a function that does that and calls a callback with the result:
(Since we're doing the work in series, we can just use
results.push(result)
since we know we won't get results out of order. In the above we could have usedresults[index] = result;
, but in some of the following examples we don't have an index to use.)Example:
(Or, again, build a wrapper for
doSomethingAsync
that gives you a promise and do the below...)If
doSomethingAsync
gives you a Promise, if you can use ES2017+ syntax (perhaps with a transpiler like Babel), you can use anasync
function withfor-of
andawait
:Example:
If you can't use ES2017+ syntax (yet), you can use a variation on the "Promise reduce" pattern (this is more complex than the usual Promise reduce because we're not passing the result from one into the next, but instead gathering up their results in an array):
Example:
...which is less cumbersome with ES2015+ arrow functions:
Example: