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`.
Another approach to return a value from an asynchronous function, is to pass in an object that will store the result from the asynchronous function.
Here is an example of the same:
I am using the
result
object to store the value during the asynchronous operation. This allows the result be available even after the asynchronous job.I use this approach a lot. I would be interested to know how well this approach works where wiring the result back through consecutive modules is involved.
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: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.
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
Using ES2017 you should have this as the function declaration
And executing it like this.
Or the Promise syntax
Short answer is, you have to implement a callback like this: