javascript - How do I return the response from an asynchronous call? -
i have function foo
makes ajax request. how can homecoming response foo
?
i tried homecoming value success
callback assigning response local variable within function , homecoming one, none of ways homecoming response.
function foo() { var result; $.ajax({ url: '...', success: function(response) { result = response; // homecoming response; // <- tried 1 } }); homecoming result; } var result = foo(); // ends beingness `undefined`.
->
more general explanation of async behavior different examples, please see why variable unaltered after modify within of function? - asynchronous code reference
->
if understand problem, skip possible solutions below.
the a in ajax stands asynchronous. means sending request (or rather receiving response) taken out of normal execution flow. in example, $.ajax
returns , next statement, return result;
, executed before function passed success
callback called.
here analogy makes difference between synchronous , asynchronous flow clearer:
synchronousimagine create phone phone call friend , inquire him you. although might take while, wait on phone , stare space, until friend gives reply needed.
the same happening when create function phone call containing "normal" code:
function finditem() { var item; while(item_not_found) { // search } homecoming item; } var item = finditem(); // item dosomethingelse();
even though finditem
might take long time execute, code coming after var item = finditem();
has wait until function returns result.
you phone call friend 1 time again same reason. time tell him in hurry , should call back on mobile phone. hang up, leave house , whatever planned do. 1 time friend calls back, dealing info gave you.
that's what's happening when ajax request.
finditem(function(item) { // item }); dosomethingelse();
instead of waiting response, execution continues , statement after ajax phone call executed. response eventually, provide function called 1 time response received, callback (notice something? call back ?). statement coming after phone call executed before callback called.
solution(s)embrace asynchronous nature of javascript! while asynchronous operations provide synchronous counterparts (so "ajax"), it's discouraged utilize them, in browser context.
why bad ask?
javascript runs in ui thread of browser , long running process lock ui, making unresponsive. additionally, there upper limit on execution time javascript , browser inquire user whether go on execution or not.
all of bad user experience. user won't able tell whether working fine or not. furthermore effect worse users slow connection.
restructure code let functions take callbacksthe improve approach organize code around callbacks. in illustration in question, can create foo
take callback , utilize success
callback. this
var result = foo(); // code depends on 'result'
becomes
foo(function(result) { // code depends on 'result' });
here pass function argument foo
. can pass function reference, example:
function mycallback(result) { // code depends on 'result' } foo(mycallback);
foo
defined follows:
function foo(callback) { $.ajax({ // ... success: callback }); }
callback
refer function pass foo
when phone call , pass on success
. i.e. 1 time ajax request successful, $.ajax
phone call callback
, pass response callback (which can referred result
, since how defined callback).
you can process response before passing callback:
function foo(callback) { $.ajax({ // ... success: function(response) { // example, filter response callback(filtered_response); } }); }
it's easier write code using callbacks seems. after all, javascript in browser heavily event driven (dom events). receiving ajax response nil else event. difficulties arise when have work 3rd party code, problems can solved thinking through application flow.
use promisesthe promise api new feature of ecmascript 6, has browser support already. there many libraries implement standard promises api , provide additional methods ease utilize , composition of asynchronous functions (e.g. bluebird).
promises containers future values. when promise receives value (it resolved) or when cancelled (rejected), notifies of "listeners" want access value.
the advantage on plain callbacks allow decouple code , easier compose.
here simple illustration of using promise:
function delay() { // `delay` returns promise homecoming new promise(function(resolve, reject) { // `delay` able resolve or reject promise settimeout(function() { resolve(42); // after 3 seconds, resolve promise value 42 }, 3000); }); } delay().then(function(v) { // `delay` returns promise console.log(v); // log value 1 time resolved }).catch(function(v) { // or else if rejected // (it not happen in example, since `reject` not called). });
applied our ajax phone call utilize promises this:
function ajax(url) { homecoming 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() { // error occurred });
describing advantages promises offer beyond scope of answer, if write new code, should consider them. provide great abstraction , separation of code.
more info promises: html5 rocks - javascript promises
jquery: utilize deferred objectsdeferred objects jquery's custom implementation of promises (before promise api standardized). behave promises, expose different api.
every ajax method of jquery returns "deferred object" (actually promise of deferred object) can homecoming function:
function ajax() { homecoming $.ajax(...); } ajax().done(function(result) { // code depending on result }).fail(function() { // error occurred });
promise gotchas
keep in mind promises , deferred objects containers future value, not value itself. example, suppose had following:
function checkpassword() { homecoming $.ajax({ url: '/password', data: { username: $('#username').val(), password: $('#password').val() }, type: 'post', datatype: 'json' }); } if (checkpassword()) { // tell user they're logged in }
this code misunderstands above asynchrony issues. specifically, $.ajax()
doesn't freeze code while checks '/password' page on server - sends request server , while waits, returns jquery ajax deferred object, not response server. means if
statement going deferred object, treat true
, , proceed though user logged in. not good.
but prepare easy:
checkpassword() .done(function(r) { if (r) { // tell user they're logged in } else { // tell user password bad } }) .fail(function(x) { // tell user bad happened });
so we're still calling '/password' page on server, our code handles wait time server respond. $.ajax()
phone call still returns jquery ajax deferred object, utilize attach event listeners .done()
, .fail()
. in .done()
call, server responded normal response (http 200), check object returned server. in illustration server returning true if login successful, false if not, if (r)
checking true/false.
in .fail()
handler we're dealing going wrong - illustration if user lost net connection while typing in username , password, or if server went down.
as mentioned, asynchronous operations have synchronous counterparts. while don't advocate there use, completeness, here how perform synchronous call:
without jqueryif straight utilize xmlhttprequest
object, pass false
3rd argument .open
.
if utilize jquery, can set async
alternative false
. note alternative deprecated since jquery 1.8. can either still utilize success
callback or access responsetext
property of jqxhr object:
function foo() { var jqxhr = $.ajax({ //... async: false }); homecoming jqxhr.responsetext; }
if utilize other jquery ajax method, such $.get
, $.getjson
, etc., have alter $.ajax
(since can pass configuration parameters $.ajax
).
heads up! not possible create synchronous jsonp request. jsonp nature asynchronous (one more reason not consider option).
javascript jquery ajax asynchronous
No comments:
Post a Comment