How do I return a response/result from a function foo
that makes an asynchronous request?
I tried to return the value from the callback and assign the result to a local variable inside the function and return that variable, but none of these methods actually return a response - they all return undefined
or some other variable## The initial value of #result is.
Example of an asynchronous function that accepts a callback (using jQuery’s ajax function):
function foo() { var result; $.ajax({ url: '...', success: function(response) { result = response; // return response; // <- I tried that one as well } }); return result; // It always returns `undefined` }
Example using Node.js:
function foo() { var result; fs.readFile("path/to/file", function(err, data) { result = data; // return data; // <- I tried that one as well }); return result; // It always returns `undefined` }
Example using then promise block:
function foo() { var result; fetch(url).then(function(response) { result = response; // return response; // <- I tried that one as well }); return result; // It always returns `undefined` }
If you don't use jQuery in your code, this answer is for you
Your code should look like this:
Felix Kling did a great job writing the answer for people using jQuery for AJAX, but I decided to provide an alternative for people who don't use jQuery.
(Note, for those using the new fetch
API, Angular or Promise, I have added another answer below
)Problems you face
The
Ain AJAX stands for asynchronous. This means that sending the request (or rather receiving the response) is removed from the normal flow of execution. In your example, .send
This means that when you return, the listener you defined has not been executed yet, which means that the value you returned has not been defined yet.code> returns immediately and the next statement is executed before calling the function you passed as the
successcallback
return result;.
(violin)
Since thea=5
One possible solution to this problem is topart has not been executed yet, the returned
avalue is
undefined. AJAX behaves in such a way that you return the value before the server has a chance to tell your browser what the value is.
reactively write code that tells your program what to do after the calculation is complete.
This is calledCPS. Basically, we pass getFive
an action to perform on completion, and we tell our code how to react when the event completes (such as our AJAX call, or in this case a timeout) .
Usage is:(violin).
Possible solutionsMake AJAX calls synchronous (we call it SJAX).-
Refactor your code to work properly with callbacks. -
1. Synchronous AJAX - Don't do it! !Don't do it! Felix's answer presents some compelling arguments as to why this is a bad idea. All in all, it freezes the user's browser until the server returns a response and creates a very bad user experience. Here's another short summary from MDN explaining why:
If youhave to do this, you can pass a flag. The specific methods are as follows:
2. Reorganization codefoo
so:accept a callback. We'll tell the code how to
reactwhen
foo completes.Here we're passing an anonymous function, but we could just as easily pass a reference to an existing function, making it look like:
For more details on how to accomplish this type of callback design, check out Felix's answer.
Now, let's define foo itself to perform the corresponding operations
(violin)
Now we have the foo function accept an action to run when AJAX completes successfully. We can extend this functionality further by checking if the response status is not 200 and taking appropriate action (creating a failure handler, etc.). It effectively solved our problem.
If you're still having trouble understanding this, Read the AJAX Get Started Guide on MDN.
question
TheA in Ajax stands for asynchronous. This means that sending the request (or rather receiving the response) is removed from the normal flow of execution. In your example,
$.ajax
returns immediately, and the next statementreturn result;
is executed before the function you pass as thesuccess
callback is even called .Here's an analogy that hopefully makes the difference between synchronous and asynchronous streams clearer:
Synchronize
Imagine you call a friend and ask him to find some information for you. Although it might take a while, you wait by the phone, staring into space, until your friend gives you the answer you need.
The same thing happens when you make a function call that contains "normal" code:
Although
findItem
may take a long time to execute, any code aftervar item = findItem();
must wait until the function returns result.asynchronous
You call your friend again for the same reason. But this time you tell him that you are anxious and he should call you back on your mobile . You hang up the phone, leave the house, and do whatever you planned to do. Once your friend calls you back, you are processing the information he gave you.
This is exactly what happens when you make an Ajax request.
Does not wait for a response, but continues execution immediately and executes the statement after the Ajax call. In order to finally get the response, you need to provide a function that is called after the response is received, a callback (notice anything? Callback?). Any statements after this call will be executed before the callback is called.
solution
Embrace the asynchronous nature of JavaScript! While some asynchronous operations provide synchronous counterparts (as does "Ajax"), their use is generally discouraged, especially in a browser context.
Why is it bad, you ask?
JavaScript runs in the browser's UI thread, and any long-running process can lock the UI, making it unresponsive. In addition, there is an upper limit on the execution time of JavaScript, and the browser will ask the user whether to continue execution.
All of these can lead to a very bad user experience. The user will not be able to tell if everything is working properly. In addition, the effect will be worse for users with slower Internet speeds.
Below we will introduce three different solutions, which all build on each other:
async/await
(ES2017, works in older browsers if you use a transpiler or regenerator)then()
(ES2015, works in older browsers if you use one of the many Promise libraries)All three features are available in current browsers and Node 7.
ES2017: Using
async/await for promises
The 2017 release of ECMAScript introduced syntax-level support for asynchronous functions. With
async
andawait
you can write asynchronously in a "synchronous style". The code is still asynchronous, but easier to read/understand.async/await
Built on Promise:async
Functions always return a Promise.await
"Unwraps" a Promise and either produces the value of the Promise as resolved, or throws an error if the Promise is rejected.IMPORTANT: You can only use
await
within an JavaScript Modules. Top-levelawait
is not supported outside of modules, so you may have to create an async IIFE (Immediately Invoked Function Expression) to start anasync
context (if not using the module).You can read about
async
andawait
.Here is an example detailing the delay function
findItem()
above:The current browser and node versions support
async/await
. You can also convert your code to ES5 with the help of regenerator (or a tool that uses regenerator) to support older environments such as Babel).Let the function accept callbacks
Callback refers to when function 1 is passed to function 2. Function 2 can call function 1 when it is ready. In the context of an asynchronous process, the callback is called whenever the asynchronous process completes. Normally, the results are passed to the callback.
In the question's example, you could make
foo
accept a callback and use it as thesuccess
callback. So thisbecame
Here we define an "inline" function, but you can pass any function reference:
foo
itself is defined as follows:callback
will reference the function we passed tofoo
when we called it, and pass it tosuccess
. ie. Once the Ajax request is successful,$.ajax
will callcallback
and pass the response to the callback (which can be referenced withresult
since that's how we define callbacks) .You can also process the response before passing it to the callback:
Writing code using callbacks is easier than it looks. After all, JavaScript in browsers is largely event-driven (DOM events). Receiving an Ajax response is nothing more than an event. Difficulties may arise when you have to use third-party code, but most problems can be solved by simply thinking about the application flow.
ES2015: with then()的 Promise >
The Promise API is a new ECMAScript 6 (ES2015) feature, but it already has good browser support. There are also many libraries that implement the standard Promises API and provide additional methods to simplify the use and composition of asynchronous functions (e.g. Bluebird).
Promise is a container for future values. When a Promise receives a value (resolved) or is canceled (rejected), it notifies all "listeners" that want to access the value.
The advantage over normal callbacks is that they allow you to decouple your code and are easier to write.
This is an example of using Promise: