如何从asynchronous调用返回响应?

我有一个函数foo ,这使得一个Ajax请求。 我怎样才能返回foo的回应?

我尝试从successcallback中返回值,并将响应分配给函数内部的局部variables,并返回该variables,但是没有一个方法实际返回响应。

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

->有关不同示例的asynchronous行为的更一般的解释,请参阅 为什么我的variables在函数内部修改后没有改变? – asynchronous代码引用

->如果您已经了解问题,请跳到下面的可能解决scheme。

问题

Ajax中的A代表asynchronous 。 这意味着发送请求(或接收响应)从正常执行stream程中取出。 在你的例子中, $.ajax立即返回,下$.ajax语句return result;successcallback被传递的函数之前被执行。

下面是一个类比,希望能够使同步和asynchronousstream程更清晰:

同步

想象一下,你打个电话给一个朋友,请他为你找点东西。 虽然可能需要一段时间,但是你等待电话,盯着太空,直到你的朋友给你你需要的答案。

当你进行包含“普通”代码的函数调用时,情况也是如此:

 function findItem() { var item; while(item_not_found) { // search } return item; } var item = findItem(); // Do something with item doSomethingElse(); 

尽pipefindItem可能需要很长时间才能执行,但在var item = findItem();之后的任何代码都会被执行var item = findItem(); 必须等到函数返回结果。

asynchronous

你再次打电话给你的朋友出于同样的原因。 但是这次你告诉他你很急,他应该用手机给你回电话。 你挂断电话,离开房子,做你计划要做的事情。 一旦你的朋友打电话给你,你正在处理他给你的信息。

这正是你做Ajax请求时发生的事情。

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

而不是等待响应,执行将立即执行,执行Ajax调用之后的语句。 为了最终得到响应,你提供了一个函数,一旦收到响应,一个callback (注意什么? callback ?)。 在调用之前执行的任何语句都会在调用callback之前执行。



解决scheme(S)

拥抱JavaScript的asynchronous性! 尽pipe某些asynchronous操作提供了同步对应(“Ajax”也是如此),但通常不鼓励使用它们,特别是在浏览器上下文中。

为什么你不好问?

JavaScript在浏览器的UI线程中运行,任何长时间运行的进程都将locking用户界面,使其无法响应。 另外,JavaScript的执行时间有一个上限,浏览器会询问用户是否继续执行。

所有这些都是非常糟糕的用户体验。 用户将无法判断一切是否正常工作。 而且,对于连接速度慢的用户来说效果会更差。

下面我们将看看三种不同的解决scheme:

  • 承诺与async/await (ES2017 +,如果您使用转播机或再生器在旧版浏览器中可用)
  • callback (在节点中stream行)
  • then()承诺 (ES2015 +,如果您使用许多承诺库之一,在旧的浏览器中可用)

所有这三个都可以在当前的浏览器和节点7+中使用。


ES2017 +:承诺与async/await

2017年发布的新版ECMAScript版本引入了对asynchronous函数的语法级支持 。 在asyncawait的帮助下,你可以用“同步风格”编写asynchronous程序。 尽pipe没有错误:代码仍然是asynchronous的,但它更容易阅读/理解。

async/awaitbuild立在承诺之上:一个async函数总是返回一个承诺。 await “解开”承诺,或者导致承诺所解决的价值,或者如果承诺被拒绝,则会产生错误。

重要说明:只能在async函数中使用await 。 这意味着,在最高层,你仍然需要直接的承诺。

您可以阅读更多关于MDN上的asyncawait

下面是一个在上面的延迟之上构build的例子:

 // 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 a second (just for the sake of this example) await delay(1000); // GET information about each book return await 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; } } // Async functions always return a promise getAllBooks() .then(function(books) { console.log(books); }); 

较新的浏览器和节点版本支持async/await 。 您还可以通过在再生器 (或使用再生器的工具,如Babel )的帮助下将代码转换为ES5来支持较旧的环境。


让函数接受callback

callback只是一个传递给另一个函数的函数。 其他函数可以调用传递的函数。 在asynchronous过程的上下文中,只要asynchronous过程完成,就会调用callback。 通常,结果传递给callback。

在问题的例子中,您可以让foo接受callback并将其用作successcallback。 所以这

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

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

这里我们定义了函数“inline”,但是你可以传递任何函数引用:

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

foo本身的定义如下:

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

callback函数是指当我们调用函数时我们传递给函数的函数,我们只是将它传递给success 。 也就是说,一旦Ajax请求成功, $.ajax将调用callback函数,并将回应传递给callback函数(可以用result来引用,因为这是我们定义的callback函数)。

您也可以在将响应传递给callback之前处理该响应:

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

使用callback编写代码比看起来更容易。 毕竟,浏览器中的JavaScript是大量事件驱动的(DOM事件)。 接收Ajax响应不是别的,而是一个事件。
当您需要使用第三方代码时,可能会遇到一些困难,但大部分问题都可以通过思考应用程序stream程来解决。


ES2015 +:承诺然后()

Promise API是ECMAScript 6(ES2015)的一个新function,但它已经有了很好的浏览器支持 。 还有许多库实现了标准的Promises API,并提供了其他方法来简化asynchronous函数(如蓝鸟 )的使用和组合。

承诺是未来价值的容器。 当promise收到这个值(被parsing )或者被取消( 被拒绝 )的时候,它会通知所有想要访问这个值的“监听者”。

与普通callback相比,它的优点是它们可以让你的代码解耦,而且更容易编写。

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

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

应用到我们的Ajax调用,我们可以使用这样的承诺:

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

描述承诺提供的所有优点超出了这个答案的范围,但是如果你编写新的代码,你应该认真考虑它们。 他们提供了一个很好的抽象和你的代码分离。

有关承诺的更多信息: HTML5的岩石 – JavaScript的承诺

附注:jQuery的延期对象

延迟对象是jQuery自定义的Promise实现(在Promise API被标准化之前)。 他们的行为几乎像承诺,但揭露了一个稍微不同的API。

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

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

附注:诺言陷阱

请记住,承诺和延期对象只是未来价值的容器 ,它们本身并不是价值。 例如,假设你有以下几点:

 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 } 

这段代码误解了上面的asynchronous问题。 具体来说, $.ajax()在检查服务器上的“/ password”页面时不会冻结代码 – 它向服务器发送请求并等待时,立即返回一个jQuery Ajax Deferred对象,而不是响应服务器。 这意味着if语句将总是得到这个Deferred对象,将其视为true ,并像用户login一样继续。不好。

但修复很简单:

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


不build议:同步“Ajax”调用

正如我所提到的,一些(!)asynchronous操作具有同步对等体。 我不主张使用它,但为了完整起见,下面是如何执行同步调用:

没有jQuery

如果直接使用XMLHTTPRequest对象, .open作为第三个parameter passing给.open

jQuery的

如果您使用jQuery ,则可以将async选项设置为false 。 请注意,从jQuery 1.8开始,这个选项已经被弃用了 。 然后,您仍然可以使用successcallback或访问jqXHR对象的responseText属性:

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

如果您使用任何其他jQuery Ajax方法(如$.get$.getJSON等),则必须将其更改为$.ajax (因为您只能将configurationparameter passing给$.ajax )。

小心! 无法创build同步的JSONP请求。 JSONP本质上总是asynchronous的(还有一个理由甚至不考虑这个选项)。

如果你没有在代码中使用jQuery,这个答案是给你的

你的代码应该是这样的:

 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)为使用jQuery for AJAX的人写了一个好的答案,我决定为那些不支持的人提供一个select。

( 请注意,对于那些使用新的fetch API,Angular或promise我已经在下面添加了另一个答案 )


你在面对什么

这是对另一个答案“问题的解释”的一个简短的总结,如果你不确定读完这个后,阅读。

AJAX中的A代表asynchronous 。 这意味着发送请求(或接收响应)从正常执行stream程中取出。 在你的例子中, .send立即返回,下.send语句return result;successcallback被传递的函数之前被执行。

这意味着当您返回时,您定义的侦听器尚未执行,这意味着您要返回的值尚未定义。

这是一个简单的比喻

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

(小提琴)

由于a=5部分尚未执行, a返回的值是undefined的。 AJAX的行为就像这样,在服务器有机会告诉浏览器这个值是什么之前,你要返回值。

解决这个问题的一个可能的方法就是重新编写代码,告诉程序在计算完成后该做什么。

 function onComplete(a){ // When the code completes, do this alert(a); } function getFive(whenDone){ var a; setTimeout(function(){ a=5; whenDone(a); },10); } 

这被称为CPS 。 基本上,当一个事件完成时(比如我们的AJAX调用,或者在这种情况下,超时),我们传递getFive一个动作,当它完成时,我们告诉我们的代码如何做出反应。

用法是:

 getFive(onComplete); 

哪个应该提醒“5”到屏幕上。 (小提琴) 。

可能的解决scheme

基本上有两种方法可以解决这个问题:

  1. 使AJAX调用同步(让我们称之为SJAX)。
  2. 重构你的代码,以callback正常工作。

1.同步AJAX – 不要这样做!

至于同步AJAX, 不要这样做! 费利克斯的回答提出了一些关于为什么这是一个坏主意的引人注目的论点。 综上所述,它会冻结用户的浏览器,直到服务器返回响应并创build非常糟糕的用户体验。 这是另外一个摘自MDN的摘要,为什么:

XMLHttpRequest支持同步和asynchronous通信。 但是,一般来说,出于性能原因,asynchronous请求应该优先于同步请求。

总之,同步请求阻止代码的执行……这可能会导致严重的问题…

如果你必须这样做,你可以通过一个标志: 这是如何:

 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.重组代码

让你的函数接受一个callback。 在这个例子中, foo可以被接受callback。 我们将告诉我们的代码当foo完成时如何反应

所以:

 var result = foo(); // code that depends on `result` goes here 

变为:

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

在这里,我们传递了一个匿名函数,但是我们可以轻松地将引用传递给现有函数,使其看起来像:

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

有关这种callbackdevise如何完成的更多细节,请查看Felix的答案。

现在,让我们定义foo本身来采取相应的行动

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

(小提琴)

现在我们已经让我们的foo函数在AJAX成功完成的时候接受一个动作来运行,我们可以通过检查响应状态是否不是200并且相应地执行(创build一个失败处理程序等)来进一步扩展它。 有效解决我们的问题。

如果您仍然很难理解,请阅读 MDN上的“AJAX入门指南” 。

XMLHttpRequest 2 (首先阅读Benjamin Gruenbaum&Felix Kling的答案)

如果你不使用jQuery,并希望在现代浏览器和移动浏览器上使用一个简短的XMLHttpRequest 2,我build议以这种方式使用它:

 function ajax(a, b, c){ // URL, callback, just a placeholder c = new XMLHttpRequest; c.open('GET', a); c.onload = b; c.send() } 

如你看到的:

  1. 它比所有列出的其他function都要短。
  2. callback直接设置(所以没有额外的不必要的closures)。
  3. 它使用新的onload(所以你不必检查readystate &&状态)
  4. 还有其他一些我不记得使XMLHttpRequest 1恼人的情况。

有两种方法可以获得此Ajax调用的响应(三个使用XMLHttpRequest var名称):

最简单的:

 this.response 

或者,如果由于某种原因, bind()callbackbind()到一个类上:

 e.target.response 

例:

 function callback(e){ console.log(this.response); } ajax('URL', callback); 

或者(上面的一个更好的匿名函数总是一个问题):

 ajax('URL', function(e){console.log(this.response)}); 

没有什么容易

现在有些人可能会说使用onreadystatechange或甚至XMLHttpRequestvariables名称更好。 那不对。

查看XMLHttpRequest高级function

它支持所有*现代浏览器。 我可以确认,因为我使用这种方法,因为XMLHttpRequest 2存在。 我从来没有任何types的问题,我使用的所有浏览器。

onreadystatechange只有在你想获得状态2的标题时才有用。

使用XMLHttpRequestvariables名称是另一个大错误,因为您需要在onload / oreadystatechange闭包中执行callback,否则您将丢失它。


现在,如果你想要使用post和FormData更复杂的东西,你可以很容易地扩展这个function:

 function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder c = new XMLHttpRequest; c.open(e||'get', a); c.onload = b; c.send(d||null) } 

再次…这是一个非常短的function,但它确实得到和发布。

用法示例:

 x(url, callback); // By default it's get so no need to set x(url, callback, 'post', {'key': 'val'}); // No need to set post data 

或传递一个完整的表单元素( document.getElementsByTagName('form')[0] ):

 var fd = new FormData(form); x(url, callback, 'post', fd); 

或者设置一些自定义值:

 var fd = new FormData(); fd.append('key', 'val') x(url, callback, 'post', fd); 

正如你所看到的,我没有实现同步…这是一件坏事。

话虽如此,为什么不这么简单呢?


正如在评论中所提到的那样,错误&&同步的使用确实彻底打破了答案的要点。 以适当的方式使用Ajax是一个很好的简短方法?

error handling程序

 function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder c = new XMLHttpRequest; c.open(e||'get', a); c.onload = b; c.onerror = error; c.send(d||null) } function error(e){ console.log('--Error--', this.type); console.log('this: ', this); console.log('Event: ', e) } function displayAjax(e){ console.log(e, this); } x('WRONGURL', displayAjax); 

在上面的脚本中,你有一个静态定义的error handling程序,所以它不会影响function。 error handling程序也可以用于其他function。

但要真正摆脱错误, 唯一的办法就是编写一个错误的URL,在这种情况下,每个浏览器都会抛出一个错误。

error handling程序可能是有用的,如果你设置自定义标题,设置响应types为BLOB数组缓冲区或任何….

即使你传递'POSTAPAPAP'作为方法,也不会抛出错误。

即使你传递'fdggdgilfdghfldj'作为formdata它不会抛出一个错误。

在第一种情况下,这个错误在this.statusTextdisplayAjax()内部,因为Method not Allowed

在第二种情况下,它只是起作用。 您必须在服务器端检查是否传递了正确的发布数据。

跨域不允许自动抛出错误。

在错误响应中,没有错误代码。

只有这个被设置为错误的types。

为什么添加一个error handling程序,如果你完全不能控制错误? 大部分的错误在callback函数displayAjax()

所以:如果您能够正确地复制和粘贴URL,则无需进行错误检查。 ;)

PS:作为第一个testing,我写了x('x',displayAjax)…,它完全得到了答复…? 所以我检查了HTML所在的文件夹,并且有一个名为“x.xml”的文件。 所以即使你忘记了XMLHttpRequest 2的文件扩展名也会find它 。 我笑了


读取文件同步

不要这样做。

如果你想阻塞浏览器一会儿加载一个不错的大txt文件同步。

 function omg(a, c){ // URL c = new XMLHttpRequest; c.open('GET', a, true); c.send(); return c; // Or c.response } 

现在你可以做了

  var res = omg('thisIsGonnaBlockThePage.txt'); 

没有其他方式以非asynchronous的方式做到这一点。 (是的,用setTimeout循环…但严重吗?)

另一点是…如果你使用API​​或只是你自己的列表文件或任何你总是使用不同的function,每个请求…

只有当你有一个页面,你总是加载相同的XML / JSON或任何你只需要一个函数。 在这种情况下,修改一下Ajax函数,并用你的特殊函数replaceb。


以上function仅供基本使用。

如果你想扩展function…

是的你可以。

我使用了很多API,并且我在每个HTML页面中集成的第一个函数是这个答案中的第一个Ajax函数,只有GET …

但是你可以用XMLHttpRequest 2做很多事情:

我做了一个下载pipe理器(在简历,文件读取器,文件系统两侧使用范围),使用canvas的各种图像resize转换器,使用base64images填充websql数据库等等…但是在这些情况下,您应该创build一个函数…有时你需要一个blob,数组缓冲区,你可以设置标题,覆盖mimetype,还有更多…

但这里的问题是如何返回一个Ajax响应…(我添加了一个简单的方法。)

您正在使用Ajax不正确。 这个想法不是让它返回任何东西,而是把数据交给一个叫做callback函数的处理数据的东西。

那是:

 function handleData( responseData ) { // Do what you want with the data console.log(responseData); } $.ajax({ url: "hi.php", ... success: function ( data, status, XHR ) { handleData(data); } }); 

在提交处理程序中返回任何内容都不会执行任何操作。 你必须改变数据,或者直接在成功函数中做你想做的事情。

最简单的解决scheme是创build一个JavaScript函数,并将其称为Ajax successcallback。

 function callServerAsync(){ $.ajax({ url: '...', success: function(response) { successCallback(response); } }); } function successCallback(responseObj){ // Do something like read the response and show data alert(JSON.stringify(responseObj)); // Only applicable to JSON response } function foo(callback) { $.ajax({ url: '...', success: function(response) { return callback(null, response); } }); } var result = foo(function(err, result){ if (!err) console.log(result); }); 

我会用一个可怕的手绘漫画来回答。 第二个图像是在代码示例中undefined result的原因。

在这里输入图像描述

Angular1

对于使用AngularJS的人来说,可以使用Promise来处理这种情况。

这里说,

承诺可以用来完全asynchronous的function,并允许将多个function链接在一起。

你也可以在这里find一个很好的解释。

在下面提到的文档中find示例。

  promiseB = promiseA.then( function onSuccess(result) { return result + 1; } ,function onError(err) { //Handle error } ); // promiseB will be resolved immediately after promiseA is resolved // and its value will be the result of promiseA incremented by 1. 

Angular2和更高版本

Angular2中看看下面的例子,但是推荐使用Angular2 Observables

  search(term: string) { return this.http .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`) .map((response) => response.json()) .toPromise(); 

}

你可以这样消费,

 search() { this.searchService.search(this.searchField.value) .then((result) => { this.result = result.artists.items; }) .catch((error) => console.error(error)); } 

在这里看到原来的post。 但是Typescript不支持本地es6 Promises ,如果你想使用它,你可能需要插件。

另外这里是承诺规格在这里定义。

Most of the answers here give useful suggestions for when you have a single async operation, but sometimes, this comes up when you need to do an asynchronous operation for each entry in an array or other list-like structure. The temptation is to do this:

 // WRONG var results = []; theArray.forEach(function(entry) { doSomethingAsync(entry, function(result) { results.push(result); }); }); console.log(results); // Eg, using them, returning them, etc. 

例:

 // WRONG var theArray = [1, 2, 3]; var results = []; theArray.forEach(function(entry) { doSomethingAsync(entry, function(result) { results.push(result); }); }); console.log("Results:", results); // Eg, using them, returning them, etc. function doSomethingAsync(value, callback) { console.log("Starting async operation for " + value); setTimeout(function() { console.log("Completing async operation for " + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } 
 .as-console-wrapper { max-height: 100% !important; } 

Have a look at this example:

 var app = angular.module('plunker', []); app.controller('MainCtrl', function($scope,$http) { var getJoke = function(){ return $http.get('http://api.icndb.com/jokes/random').then(function(res){ return res.data.value; }); } getJoke().then(function(res) { console.log(res.joke); }); }); 

As you can see getJoke is returning a resolved promise (it is resolved when returning res.data.value ). So you wait until the $http.get request is completed and then console.log(res.joke) is executed (as a normal asynchronous flow).

This is the plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

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:

 var async = require("async"); // This wires up result back to the caller var result = {}; var asyncTasks = []; asyncTasks.push(function(_callback){ // some asynchronous operation $.ajax({ url: '...', success: function(response) { result.response = response; _callback(); } }); }); async.parallel(asyncTasks, function(){ // result is available after performing asynchronous operation console.log(result) console.log('Done'); }); 

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.

While promises and callbacks work fine in many situations, it is a pain in the rear to express something like:

 if (!name) { name = async1(); } async2(name); 

You'd end up going through async1 ; check if name is undefined or not and call the callback accordingly.

 async1(name, callback) { if (name) callback(name) else { doSomething(callback) } } async1(name, async2) 

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.

 var Fiber = require('fibers') function async1(container) { var current = Fiber.current var result doSomething(function(name) { result = name fiber.run() }) Fiber.yield() return result } Fiber(function() { var name if (!name) { name = async1() } async2(name) // Make any number of async calls from here } 

You can checkout the project here .

Short answer is, you have to implement a callback like this:

 function callback(response) { // Here you can do what ever you want with the response object. console.log(response); } $.ajax({ url: "...", success: callback }); 

You can use this custom library (written using Promise) to make a remote call.

 function $http(apiConfig) { return new Promise(function (resolve, reject) { var client = new XMLHttpRequest(); client.open(apiConfig.method, apiConfig.url); client.send(); client.onload = function () { if (this.status >= 200 && this.status < 300) { // Performs the function "resolve" when this.status is equal to 2xx. // Your logic here. resolve(this.response); } else { // Performs the function "reject" when this.status is different than 2xx. reject(this.statusText); } }; client.onerror = function () { reject(this.statusText); }; }); } 

Simple usage example:

 $http({ method: 'get', url: 'google.com' }).then(function(response) { console.log(response); }, function(error) { console.log(error) }); 

The following example I have written shows how to

  • Handle asynchronous HTTP calls;
  • Wait for response from each API call;
  • Use Promise pattern;
  • Use Promise.All pattern to join multiple HTTP calls;

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.

语境。 The example is querying the Spotify Web API endpoint in order to search for playlist objects for a given set of query strings:

 [ "search?type=playlist&q=%22doom%20metal%22", "search?type=playlist&q=Adele" ] 

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 Spotify user objects and execute the new HTTP call within the ExecutionProfileBlock 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:

 -H "Authorization: Bearer {your access token}" 

So, you to run the following example you need to put your access token in the request headers:

 var spotifyAccessToken = "YourSpotifyAccessToken"; var console = { log: function(s) { document.getElementById("console").innerHTML += s + "<br/>" } } // Simple XMLHttpRequest // based on https://davidwalsh.name/xmlhttprequest SimpleRequest = { call: function(what, response) { var request; if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Internet Explorer try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // State changes request.onreadystatechange = function() { if (request.readyState === 4) { // Done if (request.status === 200) { // Complete response(request.responseText) } else response(); } } request.open('GET', what, true); request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken); request.send(null); } } //PromiseAll var promiseAll = function(items, block, done, fail) { var self = this; var promises = [], index = 0; items.forEach(function(item) { promises.push(function(item, i) { return new Promise(function(resolve, reject) { if (block) { block.apply(this, [item, index, resolve, reject]); } }); }(item, ++index)) }); Promise.all(promises).then(function AcceptHandler(results) { if (done) done(results); }, function ErrorHandler(error) { if (fail) fail(error); }); }; //promiseAll // LP: deferred execution block var ExecutionBlock = function(item, index, resolve, reject) { var url = "https://api.spotify.com/v1/" url += item; console.log( url ) SimpleRequest.call(url, function(result) { if (result) { var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) { return item.owner.href; }) resolve(profileUrls); } else { reject(new Error("call error")); } }) } arr = [ "search?type=playlist&q=%22doom%20metal%22", "search?type=playlist&q=Adele" ] promiseAll(arr, function(item, index, resolve, reject) { console.log("Making request [" + index + "]") ExecutionBlock(item, index, resolve, reject); }, function(results) { // Aggregated results console.log("All profiles received " + results.length); //console.log(JSON.stringify(results[0], null, 2)); ///// promiseall again var ExecutionProfileBlock = function(item, index, resolve, reject) { SimpleRequest.call(item, function(result) { if (result) { var obj = JSON.parse(result); resolve({ name: obj.display_name, followers: obj.followers.total, url: obj.href }); } //result }) } //ExecutionProfileBlock promiseAll(results[0], function(item, index, resolve, reject) { //console.log("Making request [" + index + "] " + item) ExecutionProfileBlock(item, index, resolve, reject); }, function(results) { // aggregated results console.log("All response received " + results.length); console.log(JSON.stringify(results, null, 2)); } , function(error) { // Error console.log(error); }) ///// }, function(error) { // Error console.log(error); }); 
 <div id="console" /> 

Another solution is to execute code via sequential executor nsynjs .

If underlying function is promisified

nsynjs will evaluate all promises sequentially, and put promise result into data property:

 function synchronousCode() { var getURL = function(url) { return window.fetch(url).data.text().data; }; var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'; console.log('received bytes:',getURL(url).length); }; nsynjs.run(synchronousCode,{},function(){ console.log('synchronousCode done'); }); 
 <script src="amaksr/nsynjs/master/nsynjs.js"></script> 

2017 answer: you can now do exactly what you want in every current browser and node 7+

This is quite simple:

  • Use the 'await' , which will tell JavaScript to await a thing that returns a promise to be resolved
  • Add the 'async' keyword to the parent function

Here's a working version of your code:

 (async function(){ var response = await superagent.get('...') console.log(response) })() 

await is supported in all current browsers and node 8

Here are some approaches to work with asynchronous requests:

  1. Browser Promise object
  2. Q – A promise library for JavaScript
  3. A+ Promises.js
  4. jQuery deferred
  5. XMLHttpRequest API
  6. Using callback concept – As implementation in first answer

Example: jQuery deferred implementation to work with multiple requests

 var App = App || {}; App = { getDataFromServer: function(){ var self = this, deferred = $.Deferred(), requests = []; requests.push($.getJSON('request/ajax/url/1')); requests.push($.getJSON('request/ajax/url/2')); $.when.apply(jQuery, requests).done(function(xhrResponse) { return deferred.resolve(xhrResponse.result); }); return deferred; }, init: function(){ this.getDataFromServer().done(_.bind(function(resp1, resp2) { // Do the operations which you wanted to do when you // get a response from Ajax, for example, log response. }, this)); } }; App.init(); 

This is one of the places which two ways data binding that's used in many new JavaScript frameworks will work greatly!

So if you are using Angular, React or any other frameworks which do two ways data binding, this issue is simply fixed, so in easy word, your result is undefined at the first stage, so you have got result = undefined before you recieve the data, then as soon as you get the result, it will updated and get assigned to the new value which is respond 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:

 $(document).ready(function(){ function foo() { $.ajax({url: "api/data", success: function(data){ fooDone(data); //after we have data, we pass it to fooDone }}); }; function fooDone(data) { console.log(data); //fooDone has the data and console.log it }; foo(); //call happens here }); 

For more information study about promises and observables which are newer ways to do this async stuffs.

Short answer : Your foo() method returns immediately, while the $ajax() call executes asynchronously after the function returns . The problem is then how or where to store the results retrieved by the async call once it returns.

Several solutions have been given in this thread. Perhaps the easiest way is to pass an object to the foo() method, and to store the results in a member of that object after the async call completes.

 function foo(result) { $.ajax({ url: '...', success: function(response) { result.response = response; // Store the async result } }); } var result = { response: null }; // Object to hold the async result foo(result); // Returns before the async completes 

Note that the call to foo() will still return nothing useful. However, the result of the async call will now be stored in result.response .

Use a callback() function inside the foo() success. Try in this way. It is simple and easy to understand.

 var lat = ""; var lon = ""; function callback(data){ lat = data.lat; lon = data.lon; } function getLoc() { var url = "http://ip-api.com/json" $.getJSON(url, function(data) { callback(data); }); } getLoc(); 

It's a very common issue we face while struggling with the 'mysteries' of JavaScript. Let me try demystifying this mystery today.

Let's start with a simple JavaScript function:

 function foo(){ // do something return 'wohoo'; } let bar = foo(); // bar is 'wohoo' here 

That's a simple synchronous function call (where each line of code executed one after other in sequence), and the result is same as expected.

Now let's add a bit of twist, by introducing little delay in our function, so that all lines of code are not executed in sequence. Thus, it will emulate the asynchronous behavior of function :

 function foo(){ setTimeout( ()=>{ return 'wohoo'; }, 1000 ) } let bar = foo() // bar is undefined here 

So there you go, that delay just broke the functionality we expected ! But what exactly happened ? Well, it's actually pretty logical if you look at the code. the function foo() , upon execution, returns nothing (thus returned value is undefined ), but it does start a timer, which executes a function after 1s to return 'wohoo'. But as you can see, the value that's assigned to bar is the immediately returned stuff from foo(), not anything else that comes later.

So, how do we tack this issue?

Let's ask our function for a PROMISE . Promise is really about what it means : it means that the function guarantees you to provide with any output it gets in future. so let's see it in action for our little problem above :

 function foo(){ return new Promise( (resolve, reject) => { setTimeout ( function(){ resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo' }, 1000 ) }) } let bar ; foo().then( res => { bar = res; console.log(bar) // will print 'wohoo' }); 

Thus, the summary is – to tackle the asynchronous functions like ajax based calls etc., you can use a promise to resolve the value (which you intend to return). Thus, in short you resolve value instead of returning , in asynchronous functions.

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:

 function foo() { var result; $.ajax({ url: '...', success: function(response) { myCallback(response); } }); return result; } function myCallback(response) { // Does something. } 

Let's see the forest first before looking at the trees.

There are many informative answers with great details here, I won't repeat any of them. The key to programming in JavaScript is having first the correct mental model of overall execution.

  1. Your entry point(s) is executed as the result of an event. For example, a script tag with code is loaded into the browser. (Accordingly, this is why you may need to be concerned with the readiness of the page to run your code if it requires dom elements to be constructed first, etc.)
  2. Your code executes to completion–however many asynchronous calls it makes–without executing any of your callbacks, including XHR requests, set timeouts, dom event handlers, etc. Each of those callbacks waiting to be executed will sit in a queue, waiting their turn to be run after other events that fired have all finished execution.
  3. Each individual callback to an XHR request, set timeout or dom the event once invoked will then run to completion.

The good news is that if you understand this point well, you will never have to worry about race conditions. You should first and foremost thing of how you want to organize your code as essentially the response to different discrete events, and how you want to thread them together into a logical sequence. You can use promises or higher level new async/await as tools to that end, or you can roll your own.

But you shouldn't use any tactical tools to solve a problem until you are comfortable with the actual problem domain. Draw a map of these dependencies to know what needs to run when. Attempting an ad-hoc approach to all these callbacks is just not going to serve you well.

To handle any ajax, promise, setTimeout check promise in JavaScript. $.Deferred in jQuery. Here is how you can handle the response return.

 function foo() { var result = $.ajax({ url: '...', // This success callback is not required. You can remove it. // Since we are getting at (1) success: function (response) { // result = response; } }); return result; } // Since this is a Promise object called $Deferred in jQuery // You can attach a then callback which accepts two callback // first one success and second one error foo().then( function( response ) { // This is success callback (1) console.log('success: ', response); }, function( xhr ) { // This is error callback console.log('error: ', xhr); } ) 

I had same problem, results were always undefined. In the following way, I got the response back.

 foo(123).success(function (response) { console.log(response); var result = response; //<- This is the response from ajax request. }); //ajax call function foo(id) { return $.ajax({ url: '/user/foo', data: {id: id}, dataType: 'json', type: "POST" }); } 

Instead of success() , can use done() also.

 foo(123).done(function (response) { console.log(response); });