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`.
Short answer: Your
foo()
method returns immediately, while the$ajax()
call executes asynchronously after the function returns. The problem is then how or where to store the results retrieved by the async call once it returns.Several solutions have been given in this thread. Perhaps the easiest way is to pass an object to the
foo()
method, and to store the results in a member of that object after the async call completes.Note that the call to
foo()
will still return nothing useful. However, the result of the async call will now be stored inresult.response
.We find ourselves in a universe which appears to progress along a dimension we call "time". We don't really understand what time is, but we have developed abstractions and vocabulary that let us reason and talk about it: "past", "present", "future", "before", "after".
The computer systems we build--more and more--have time as an important dimension. Certain things are set up to happen in the future. Then other things need to happen after those first things eventually occur. This is the basic notion called "asynchronicity". In our increasingly networked world, the most common case of asynchronicity is waiting for some remote system to respond to some request.
Consider an example. You call the milkman and order some milk. When it comes, you want to put it in your coffee. You can't put the milk in your coffee right now, because it is not here yet. You have to wait for it to come before putting it in your coffee. In other words, the following won't work:
Because JS has no way to know that it needs to wait for
order_milk
to finish before it executesput_in_coffee
. In other words, it does not know thatorder_milk
is asynchronous--is something that is not going to result in milk until some future time. JS, and other declarative languages execute one statement after another without waiting.The classic JS approach to this problem, taking advantage of the fact that JS supports functions as first-class objects which can be passed around, is to pass a function as a parameter to the asynchronous request, which it will then invoke when it has completed its task sometime in the future. That is the "callback" approach. It looks like this:
order_milk
kicks off, orders the milk, then, when and only when it arrives, it invokesput_in_coffee
.The problem with this callback approach is that it pollutes the normal semantics of a function reporting its result with
return
; instead, functions must not reports their results by calling a callback given as a parameter. Also, this approach can rapidly become unwieldy when dealing with longer sequences of events. For example, let's say that I want to wait for the milk to be put in the coffee, and then and only then perform a third step, namely drinking the coffee. I end up needing to write something like this:where I am passing to
put_in_coffee
both the milk to put in it, and also the action (drink_coffee
) to execute once the milk has been put in. Such code becomes hard to write, and read, and debug.In this case, we could rewrite the code in the question as:
Enter promises
This was the motivation for the notion of a "promise", which is a particular type of value which represents a future or asynchronous outcome of some sort. It can represent something that already happened, or that is going to happen in the future, or might never happen at all. Promises have a single method, named
then
, to which you pass an action to be executed when the outcome the promise represents has been realized.In the case of our milk and coffee, we design
order_milk
to return a promise for the milk arriving, then specifyput_in_coffee
as athen
action, as follows:One advantage of this is that we can string these together to create sequences of future occurrences ("chaining"):
Let's apply promises to your particular problem. We will wrap our request logic inside a function, which returns a promise:
Actually, all we've done is added a
return
to the call to$.ajax
. This works because jQuery's$.ajax
already returns a kind of promise-like thing. (In practice, without getting into details, we would prefer to wrap this call so as for return a real promise, or use some alternative to$.ajax
that does so.) Now, if we want to load the file and wait for it to finish and then do something, we can simply sayfor instance,
When using promises, we end up passing lots of functions into
then
, so it's often helpful to use the more compact ES6-style arrow functions:The
async
keywordBut there's still something vaguely dissatisfying about having to write code one way if synchronous and a quite different way if asynchronous. For synchronous, we write
but if
a
is asynchronous, with promises we have to writeAbove, we said, "JS has no way to know that it needs to wait for the first call to finish before it executes the second". Wouldn't it be nice if there was some way to tell JS that? It turns out that there is--the
await
keyword, used inside a special type of function called an "async" function. This feature is part of the upcoming version of ES but is already available in transpilers such as Babel given the right presets. This allows us to simply writeIn your case, you would be able to write something like
The question was:
which CAN be interpreted as:
The solution will be to avoid callbacks, and use a combination of Promises and async/await.
I would like to give an example for a Ajax request.
(Although it can be written in Javascript, I prefer to write it in Python, and compile it to Javascript using Transcrypt. It will be clear enough.)
Lets first enable JQuery usage, to have
$
available asS
:Define a function which returns a Promise, in this case an Ajax call:
Use the asynchronous code as if it were synchronous:
Let's see the forest first before looking at the trees.
There are many informative answers with great details here, I won't repeat any of them. The key to programming in JavaScript is having first the correct mental model of overall execution.
The good news is that if you understand this point well, you will never have to worry about race conditions. You should first and foremost thing of how you want to organize your code as essentially the response to different discrete events, and how you want to thread them together into a logical sequence. You can use promises or higher level new async/await as tools to that end, or you can roll your own.
But you shouldn't use any tactical tools to solve a problem until you are comfortable with the actual problem domain. Draw a map of these dependencies to know what needs to run when. Attempting an ad-hoc approach to all these callbacks is just not going to serve you well.
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
2017 answer: you can now do exactly what you want in every current browser and node
This is quite simple:
Here's a working version of your code:
await is supported in all current browsers and node 8