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`.
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 following example I have written shows how to
This working example is self-contained. It will define a simple request object that uses the window
XMLHttpRequest
object to make calls. It will define a simple function to wait for a bunch of promises to be completed.Context. The example is querying the Spotify Web API endpoint in order to search for
playlist
objects for a given set of query strings:For each item, a new Promise will fire a block -
ExecutionBlock
, parse the result, schedule a new set of promises based on the result array, that is a list of Spotifyuser
objects and execute the new HTTP call within theExecutionProfileBlock
asynchronously.You can then see a nested Promise structure, that lets you spawn multiple and completely asynchronous nested HTTP calls, and join the results from each subset of calls through
Promise.all
.NOTE Recent Spotify
search
APIs will require an access token to be specified in the request headers:So, you to run the following example you need to put your access token in the request headers:
I have extensively discussed this solution here.
ECMAScript 6 has 'generators' which allow you to easily program in an asynchronous style.
To run the above code you do this:
If you need to target browsers that don't support ES6 you can run the code through Babel or closure-compiler to generate ECMAScript 5.
The callback
...args
are wrapped in an array and destructured when you read them so that the pattern can cope with callbacks that have multiple arguments. For example with node fs:Rather than throwing code at you, there are 2 concepts that are key to understanding how JS handles callbacks and asynchronicity. (is that even a word?)
The Event Loop and Concurrency Model
There are three things you need to be aware of; The queue; the event loop and the stack
In broad, simplistic terms, the event loop is like the project manager, it is constantly listening for any functions that want to run and communicates between the queue and the stack.
Once it receives a message to run something it adds it to the queue. The queue is the list of things that are waiting to execute (like your AJAX request). imagine it like this:
When one of these messages is going to execute it pops the message from the queue and creates a stack, the stack is everything JS needs to execute to perform the instruction in the message. So in our example it's being told to call
foobarFunc
So anything that foobarFunc needs to execute (in our case
anotherFunction
) will get pushed onto the stack. executed, and then forgotten about - the event loop will then move onto the next thing in the queue (or listen for messages)The key thing here is the order of execution. That is
WHEN is something going to run
When you make a call using AJAX to an external party or run any asynchronous code (a setTimeout for example), Javascript is dependant upon a response before it can proceed.
The big question is when will it get the response? The answer is we don't know - so the event loop is waiting for that message to say "hey run me". If JS just waited around for that message synchronously your app would freeze and it will suck. So JS carries on executing the next item in the queue whilst waiting for the message to get added back to the queue.
That's why with asynchronous functionality we use things called callbacks. It's kinda like a promise quite literally. As in I promise to return something at some point jQuery uses specific callbacks called
deffered.done
deffered.fail
anddeffered.always
(amongst others). You can see them all hereSo what you need to do is pass a function that is promised to execute at some point with data that is passed to it.
Because a callback is not executed immediately but at a later time it's important to pass the reference to the function not it executed. so
so most of the time (but not always) you'll pass
foo
notfoo()
Hopefully that will make some sense. When you encounter things like this that seem confusing - i highly recommend reading the documentation fully to at least get an understanding of it. It will make you a much better developer.
This is one of the places which two ways data binding that's used in many new JavaScript frameworks will work great for you...
So if you are using Angular, React or any other frameworks which do two ways data binding or store concept this issue is simply fixed for you, so in easy word, your result is
undefined
at the first stage, so you have gotresult = undefined
before you receive the data, then as soon as you get the result, it will be updated and get assigned to the new value which response of your Ajax call...But how you can do it in pure javascript or jQuery for example as you asked in this question?
You can use a callback, promise and recently observable to handle it for you, for example in promises we have some function like success() or then() which will be executed when your data is ready for you, same with callback or subscribe function on observable.
For example in your case which you are using jQuery, you can do something like this:
For more information study about promises and observables which are newer ways to do this async stuffs.
While promises and callbacks work fine in many situations, it is a pain in the rear to express something like:
You'd end up going through
async1
; check ifname
is undefined or not and call the callback accordingly.While it is okay in small examples it gets annoying when you have a lot of similar cases and error handling involved.
Fibers
helps in solving the issue.You can checkout the project here.