我如何返回一个异步调用的响应? [英] How do I return the response from an asynchronous call?

查看:583
本文介绍了我如何返回一个异步调用的响应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数这使得一个Ajax请求。我怎样才能回到从

响应

我试图返回从成功回调的价值和分配响应函数内部的局部变量,并返回的那一个,但没有这些方法的实际返回的响应。

 函数foo(){
    VAR的结果;

    $阿贾克斯({
        网址:'...',
        成功:函数(响应){
            结果=响应;
            //返回响应; //<  - 我想,一个以及
        }
    });

    返回结果;
}

VAR的结果= FOO(); //它总是最终被`undefined`。
 

解决方案
  

- > 有关与不同的例子异步行为更一般的说明,请参阅的的为什么我的变量不变后,我修改它的函数里面? - 异步$ C $

C基准      

- > 如果您已经了解这个问题,跳到下面可能的解决方案

问题的解释

A 在阿贾克斯代表的同步。这意味着发送请求(或者说接收响应)被取出的正常执行流程的。在你的榜样, $ AJAX 立即返回,并在接下来的声明中,返回结果; ,是在函数之前执行您通过为成功回调,甚至调用。

下面是一个比喻这有望使之间的同步和异步流程更清晰的区别:

同步

想象一下,你打个电话给朋友,请他来查找东西给你。虽然这可能需要一段时间,你在手机上等待,盯着进入太空,直到你的朋友给你你所需要的答案。

当你做一个函数调用包含同样是发生正常code:

 函数则findItem(){
    VAR项目;
    而(item_not_found){
        // 搜索
    }
    返回的项目;
}

VAR项目=则findItem();

//做一些与项目
doSomethingElse();
 

尽管则findItem 可能需要很长的时间来执行,任何code VAR项目后,即将=则findItem(); 具有的的,直到函数返回的结果。

异步

您再打电话给你的朋友出于同样的原因。但是,这一次你告诉他,你是在赶时间,他应该的给你回电话的手机上。挂断电话后,离开了房子,你打算什么做的。一旦你的朋友叫你回来了,你处理的是他给你的信息。

这正是发生了什么,当你做一个Ajax请求。

 则findItem(函数(项目){
    //做一些与项目
});
doSomethingElse();
 

而不是等待响应,继续执行,并立即执行Ajax调用后声明。为了获得响应,最终,你提供一次收到的响应函数被调用,一个的回调的(注意些什么呢?回调的?)。在执行任何语句调用后即将回调之前调用。


解决方案(S)

拥抱JavaScript的异步特性!虽然某些异步操作提供了同步同行(这样做阿贾克斯),它通常不鼓励使用它们,特别是在浏览器环境。

为什么不好你问这个干什么?

的JavaScript运行在浏览器和长时间运行的过程中会锁定用户界面的UI线程,使其反应迟钝。此外,还有在执行时间JavaScript和浏览器将询问用户是否继续执行或不上限。

这一切是非常糟糕的用户体验。用户将无法知道是否一切工作正常与否。此外,效果会差一些,为用户提供一个缓慢的连接。

重组code

让函数接受回调

更好的方法是正确地组织你的code左右的回调。在这个问题的例子,可以使接受一个回调,并把它作为成功回调。所以这个

  VAR的结果= FOO();
// code,它依赖于'结果'
 

变为

  FOO(函数(结果){
    // code,它依赖于'结果'
});
 

下面我们通过一个函数作为参数传递给。您可以传递任何函数的引用,例如:

 函数myCallBack函数(结果){
    // code,它依赖于'结果'
}

FOO(myCallBack函数);
 

本身的定义如下:

 函数foo(回调){
    $阿贾克斯({
        // ...
        成功:回调
    });
}
 

回调将把我们传递给函数当我们调用它,我们只是把它传给成功。即一旦Ajax请求是成功的, $。阿贾克斯将调用回调并通过响应回调(可以是提到与结果,因为这是我们如何定义的回调)。

您也可以将它传递给回调之前处理响应:

 函数foo(回调){
    $阿贾克斯({
        // ...
        成功:函数(响应){
            //例如,过滤反应
            回调(filtered_response);
        }
    });
}
 

它更容易编写使用的回调似乎比code。毕竟,的JavaScript在浏览器中被严重事件驱动(DOM事件)。接收Ajax响应是什么都没有,但一个事件。
可能出现的困难,当你有与第三方code的工作,但大多数问题都可以通过应用程序流只是想着来解决。

使用承诺

的<一个href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise API 是ECMAScript中6的新功能,但它有很好的浏览器支持了。还有一些实施的标准承诺的API,并提供了其他方法来缓解异步函数的使用和成分(如蓝鸟)许多图书馆

承诺是容器的的未来的值。当承诺接收值(它的解决的),或者当它被取消(拒绝的),它会通知所有谁想要访问这个值的听众。

在平原回调的优点是它们允许你做的解耦code和他们更容易编写。

下面是一个使用一个承诺的一个简单的例子:

 功能延迟(){
  //`delay`返回一个承诺
  返回新的承诺(功能(决心,拒绝){
    //只有`delay`能够解决或拒绝承诺
    的setTimeout(函数(){
      解析(42); // 3秒钟后,解决与值42的承诺
    },3000);
  });
}

延时(),然后(函数(V){//`delay`返回一个承诺
  的console.log(五); //登录值一旦解决
}),赶上(函数(五){
  //或做其他事,如果它被拒绝
  //(它不会发生在这个例子中,由于`reject`不叫)。
});
 

适用于我们的Ajax调用,我们可以使用的承诺是这样的:

 函数AJAX(URL){
  返回新的承诺(功能(决心,拒绝){
    VAR XHR =新XMLHtt prequest();
    xhr.onload =功能(){
      解析(this.responseText);
    };
    xhr.onerror =拒绝;
    xhr.open(GET,URL);
    xhr.send();
  });
}

阿贾克斯(/回声/ JSON),然后(函数(结果){
  // code根据结果
}),赶上(函数(){
  //出现错误
});
 

描述所有承诺提供的优势已经超出了这个答案的范围,但如果你写新的code,你应该认真考虑他们。他们提供的code一个伟大的抽象和分离。

有关承诺的更多信息: HTML5岩 - JavaScript的诺言

的jQuery:使用延迟对象

递延对象是jQuery的自定义实现诺言(无极API之前被标准化)。他们的行为就像承诺,但暴露出一个略有不同的API。

的jQuery的每一个Ajax的方法已经返回延迟对象(一个延迟的对象的实际承诺),它你可以从你的函数返回:

 函数阿贾克斯(){
    返回$阿贾克斯(...);
}

阿贾克斯()。完成(功能(结果){
    // code根据结果
}),失败(函数(){
    //出现错误
});
 

无极陷阱

请记住,承诺和递延对象是刚刚的容器的的未来价值,他们是不是值本身。例如,假设你有以下内容:

 函数checkPassword()所在{
    返回$阿贾克斯({
        网址:'/密码,
        数据: {
            用户名:$('#用户名)VAL()。
            密码:$('#密码)VAL()。
        },
        键入:POST,
        数据类型:JSON
    });
}

如果(checkPassword()所在){
    //告诉他们在登录的用户
}
 

这code误解了上述不同步的问题。具体而言, $阿贾克斯()不会冻结code,而它会检查你的服务器上的/密码页面 - 它发送一个请求到服务器,同时它也在等待,立即返回一个jQuery的Ajax延迟的对象,而不是从服务器的响应。这意味着如果语句将始终得到这个Deferred对象,把它当作,并继续就好像用户登录,不是很好。

但解决方法是简单的:

  checkPassword()所在
.done(函数(R){
    如果(r)的{
        //告诉他们在登录的用户
    } 其他 {
        //告诉用户他们的密码是坏
    }
})
.fail(函数(X){
    //告诉用户坏事发生
});
 

所以,现在我们仍在调用服务器上的/密码的页面,但我们的code现在正确处理的等待时间服务器响应。该 $。阿贾克斯()呼叫仍与一个jQuery的Ajax延迟的对象立即返回,但我们用它来事件侦听器附加到 .done() .fail()。在 .done()通话,这样的服务器的正常反应(HTTP 200)作出回应,我们检查由服务器返回的对象。在这个例子中,服务器只返回true,如果登录成功,否则为false,那么如果(R)正在检查真/假。

.fail()处理程序,我们正在处理一些事情出错 - 例如,如果用户丢失了他们的互联网连接,而他们是在自己的用户名和密码输入,或者如果你的服务器去了。


不推荐:同步Ajax的要求

正如我提到的,一些异步操作有同步同行。虽然我不主张有使用,完整性,这里是你将如何执行同步调用:

如果没有的jQuery

如果你直接使用 XMLHTT prequest 对象,通过作为第三个参数的 。开

的jQuery

如果您使用 jQuery的,您可以设置异步选项。请注意,此选项的德precated 的,因为jQuery的1.8。 然后,您可以仍然使用成功回调或访问的的responseText 属性=HTTP:/ /api.jquery.com/jQuery.ajax/#jqXHR">jqXHR对象

 函数foo(){
    VAR jqXHR = $阿贾克斯({
        // ...
        异步:假的
    });
    返回jqXHR.responseText;
}
 

如果你使用任何其他的jQuery的Ajax方法,如 $。获得 $。的getJSON 等。 ,你必须将其更改为 $。阿贾克斯(因为你只能通过配置参数 $。阿贾克斯)。

注意!这是不可能做出同步 JSONP 请求。 JSONP其本质始终是异步的(多了一个理由,甚至没有考虑这个选项)。

I have a function foo which makes an Ajax request. How can I return the response from foo?

I tried to return the value from the success callback as well as assigning the response to a local variable inside the function and return 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`.

解决方案

-> For a more general explanation of async behavior with different examples, please see Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

-> If you already understand the problem, skip to the possible solutions below.

Explanation of the problem

The A in Ajax stands for asynchronous. That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example, $.ajax returns immediately and the next statement, return result;, is executed before the function you passed as success callback was even called.

Here is an analogy which hopefully makes the difference between synchronous and asynchronous flow clearer:

Synchronous

Imagine you make a phone call to a friend and ask him to look something up for you. Although it might take a while, you wait on the phone and stare into space, until your friend gives you the answer you needed.

The same is happening when you make a function call containing "normal" code:

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

var item = findItem();

// Do something with item
doSomethingElse();

Even though findItem might take a long time to execute, any code coming after var item = findItem(); has to wait until the function returns the result.

Asynchronous

You call your friend again for the same reason. But this time you tell him that you are in a hurry and he should call you back on your mobile phone. You hang up, leave the house and do whatever you planned to do. Once your friend calls you back, you are dealing with the information he gave to you.

That's exactly what's happening when you do an Ajax request.

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

Instead of waiting for the response, the execution continues immediately and the statement after the Ajax call is executed. To get the response eventually, you provide a function to be called once the response was received, a callback (notice something? call back ?). Any statement coming after that call is executed before the callback is called.


Solution(s)

Embrace the asynchronous nature of JavaScript! While certain asynchronous operations provide synchronous counterparts (so does "Ajax"), it's generally discouraged to use them, especially in a browser context.

Why is it bad do you ask?

JavaScript runs in the UI thread of the browser and any long running process will lock the UI, making it unresponsive. Additionally, there is an upper limit on the execution time for JavaScript and the browser will ask the user whether to continue the execution or not.

All of this is really bad user experience. The user won't be able to tell whether everything is working fine or not. Furthermore the effect will be worse for users with a slow connection.

Restructure code

Let functions accept callbacks

The better approach is to organize your code properly around callbacks. In the example in the question, you can make foo accept a callback and use it as success callback. So this

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

becomes

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

Here we pass a function as argument to foo. You can pass any function reference, for example:

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

foo(myCallback);

foo itself is defined as follows:

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

callback will refer to the function we pass to foo when we call it and we simply pass it on to success. I.e. once the Ajax request is successful, $.ajax will call callback and pass the response to the callback (which can be referred to with result, since this is how we defined the callback).

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);
        }
    });
}

It's easier to write code using callbacks than it seems. After all, JavaScript in the browser is heavily event driven (DOM events). Receiving the Ajax response is nothing else but an event.
Difficulties could arise when you have to work with third party code, but most problems can be solved by just thinking through the application flow.

Use promises

The Promise API is a new feature of ECMAScript 6, but it has good browser support already. There are also many libraries which implement the standard Promises API and provide additional methods to ease the use and composition of asynchronous functions (e.g. bluebird).

Promises are containers for future values. When the promise receives the value (it is resolved) or when it is cancelled (rejected), it notifies all of its "listeners" who want to access this value.

The advantage over plain callbacks is that they allow you do decouple your code and they are easier to compose.

Here is a simple example of using 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);
  });
}

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).
});

Applied to our Ajax call we could use promises like this:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json").then(function(result) {
  // Code depending on result
}).catch(function() {
  // An error occurred
});

Describing all the advantages that promises offer is beyond the scope of this answer, but if you write new code, you should seriously consider them. They provide a great abstraction and separation of your code.

More information about promises: HTML5 rocks - JavaScript Promises

jQuery: Use deferred objects

Deferred objects are jQuery's custom implementation of promises (before the Promise API was standardized). They behave almost like promises, but expose a slightly different API.

Every Ajax method of jQuery already returns a "deferred object" (actually a promise of a deferred object) which you can just return from your function:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Promise gotchas

Keep in mind that promises and deferred objects are just containers for a future value, they are not the value itself. For example, suppose you had the following:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val()
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

This code misunderstands the above asynchrony issues. Specifically, $.ajax() doesn't freeze the code while it checks the '/password' page on your server - it sends a request to the server and while it waits, immediately returns a jQuery Ajax Deferred object, not the response from the server. That means the if statement is going to always get this Deferred object, treat it as true, and proceed as though the user is logged in. Not good.

But the fix is easy:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

So now we're still calling the '/password' page on the server, but our code now properly handles the wait time for the server to respond. The $.ajax() call still returns immediately with a jQuery Ajax Deferred object, but we use it to attach event listeners to .done() and .fail(). In the .done() call, where the server responded with a normal response (HTTP 200), we check the object returned by the server. In this example the server is just returning true if the login was successful, false if not, so if (r) is checking for true/false.

In the .fail() handler we're dealing with something going wrong - for example if the user lost their internet connection while they were typing in their username and password, or if your server went down.


Not recommended: Synchronous "Ajax" calls

As I mentioned, some asynchronous operations have synchronous counterparts. While I don't advocate there use, for completeness, here is how you would perform a synchronous call:

Without jQuery

If you directly use a XMLHTTPRequest object, pass false as third argument to .open.

jQuery

If you use jQuery, you can set the async option to false. Note that this option is deprecated since jQuery 1.8. You can then either still use a success callback or access the responseText property of the jqXHR object:

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

If you use any other jQuery Ajax method, such as $.get, $.getJSON, etc., you have to change it to $.ajax (since you can only pass configuration parameters to $.ajax).

Heads up! It is not possible to make a synchronous JSONP request. JSONP by its very nature is always asynchronous (one more reason to not even consider this option).

这篇关于我如何返回一个异步调用的响应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆