How to return response from async call?
P粉668113768
P粉668113768 2023-08-23 12:49:32
0
2
606
<p>How do I return a response/result from a function <code>foo</code> that makes an asynchronous request? </p> <p>I tried returning the value from the callback and assigning the result to a local variable within the function and returning that variable, but none of these methods actually return a response - they all return <code>undefined< /code> or a variable The initial value of <code>result</code>. </code></p><code> <p><strong>Example of an asynchronous function that accepts a callback</strong> (using jQuery's <code>ajax</code> function): </p> <pre class="brush:php;toolbar:false;">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` }</pre> <p><strong>Example using Node.js:</strong></p> <pre class="brush:php;toolbar:false;">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` }</pre> <p><strong>Example of <code>then</code> block using Promise: </strong></p> <pre class="brush:php;toolbar:false;">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` }</pre> <p><br /></p></code>
P粉668113768
P粉668113768

reply all(2)
P粉334721359

If you don't use jQuery in your code, this answer is for you

Your code should look like this:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // Always ends up being 'undefined'

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

Here is a short summary of the "explanation of the question" from another answer, if you are not sure after reading this, please read that answer.

The

A

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, .send code> returns immediately and the next statement is executed before calling the function you passed as the success callback return result; .

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.

This is a simple analogy:

function getFive(){
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(violin)

Since the

a=5 part has not been executed yet, the returned a value 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.

One possible solution to this problem is to

reactively write code that tells your program what to do after the calculation is complete.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}
This is called

CPS. 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:

getFive(onComplete);
"5" will be prompted on the screen.

(violin).

Possible solutions

There are basically two ways to solve this problem:

    Make AJAX calls synchronous (we call it SJAX).
  1. Refactor your code to work properly with callbacks.
1. Synchronous AJAX - Don't do it! !

As for synchronous AJAX,

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 you

have to do this, you can pass a flag. The specific methods are as follows:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}
2. Reorganization code

Make your function accept callbacks. In the example code, you can make

foo accept a callback. We'll tell the code how to react when foo completes.

so:

var result = foo();
// Code that depends on `result` goes here
become:

foo(function(result) {
    // Code that depends on `result`
});

Here we're passing an anonymous function, but we could just as easily pass a reference to an existing function, making it look like:

function myHandler(result) {
    // Code that depends on `result`
}
foo(myHandler);

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

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // When the request is loaded
       callback(httpRequest.responseText);// We're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(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.

P粉642920522

question

The

A 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 statement return result; is executed before the function you pass as the success 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:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Although findItem may take a long time to execute, any code after var 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.

findItem(function(item) {
    // Do something with the item
});
doSomethingElse();

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:

  • Promise with async/await (ES2017, works in older browsers if you use a transpiler or regenerator)
  • Callback (popular in node)
  • Promise with 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 and await 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-level await is not supported outside of modules, so you may have to create an async IIFE (Immediately Invoked Function Expression) to start an async context (if not using the module).

You can read about async and await.

Here is an example detailing the delay function findItem() above:

// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

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 the success callback. So this

var result = foo();
// Code that depends on 'result'

became

foo(function(result) {
    // Code that depends on 'result'
});

Here we define an "inline" function, but you can pass any function reference:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo itself is defined as follows:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callback will reference the function we passed to foo when we called it, and pass it to success. ie. Once the Ajax request is successful, $.ajax will call callback and pass the response to the callback (which can be referenced with result since that's how we define callbacks) .

You can also process the response before passing it to the callback:

function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

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:

function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected
    // (it would not happen in this example, since `reject` is not called).
  });
.as-console-wrapper { max-height: 100% !important; top: 0; }
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template