Bir asinxron zəngdən bir cavab necə qaytarılır?

Ajax sorğusu verən bir foo funksiyası var. foo cavabını necə qaytarmaq olar?

Mən də success geri çağırışından bir dəyər qaytarmağa çalışdım və funksiyanı içərisində yerli bir dəyişənə cavab verdim və geri verdim, amma bu metodlardan heç biri cavab vermədi.

 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`. 
4637
08 янв. Felix Kling Jan 08- ni təyin etdi 2013-01-08 20:06 '13 at 20:06 2013-01-08 20:06
@ 39 cavab
  • 1
  • 2

→ Müxtəlif nümunələrdə asynchronous davranışın daha ümumi bir izahı üçün baxın, bir funksiyanı içəridən dəyişdikdən sonra mənim dəyişən dəyişməyəcəkdir? - kodu asynchronous keçid

→ Əgər problemi artıq anladınızsa, aşağıdakı mümkün həllərə keçin.

Bu problem

Ajax -da Asynxron deməkdir. Demək ki, bir sorğu göndərmək (və ya əksinə cavab almaq) normal axından çıxarılır. Məsələn, $.ajax dərhal qaytarır və növbəti bəyan return result; , success çağırışı olaraq keçdiyiniz funksiyadan əvvəl də icra edilirdi.

İnşallah, synchronous və asynchronous axın arasındakı fərqi aydınlaşdırmaqla bir oxşarlıq var:

sinxron

Təsəvvür edin ki, sən bir dost axtarırsan və sənin üçün bir şey tapmağı xahiş edirsən. Bir müddət olmasına baxmayaraq, telefonunuzda gözləyin və dostunuz sizə lazım olan cavabı verənə qədər kosmosa baxın.

"Normal" kodunu ehtiva edən bir funksiya çağırışı edərkən eyni şey olur:

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

findItem icra etmək üçün uzun müddət findItem , var item = findItem(); funksiya nəticə verməyincə gözləməlidir.

Asynchronous

Eyni səbəbdən dostunuzu yenidən çağırırsınız. Amma bu dəfə ona tələsik olduğunuzu bildirirsiniz və sizi mobil telefonunuza geri çağırmalıdır. Siz asmaq, evdən çıxmaq və planladığınız hər şeyi edə bilərsiniz. Dostunuz sizi geri çağırır kimi, sizə verdiyi məlumatlarla məşğul olacaqsınız.

Ajax sorğusunu etdiyinizdə bu tam olaraq budur.

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

Cavab gözləmək əvəzinə icra dərhal davam edir və bəyanat Ajax zəngindən sonra icra edilir. Son olaraq cavab ala bilmək üçün cavab aldıktan sonra çağırılacaq funksiyanı təmin edəcəksiniz, bir geri çağırış (bir şey qeyd edin, "geri çağırın"). Bu zəngdən sonra hər hansı bir bəyanat geri çağırış zəngindən əvvəl həyata keçirilir.


Həll (lər)

Javascriptin asinxron xarakterini qəbul edin! Bəzi asinxron əməliyyatlar sinxron tərəfdaşları ("Ajax" kimi) təmin etsə də, adətən, brauzerin kontekstində istifadə üçün tövsiyə edilmir.

Niyə bu pisdir?

JavaScript brauzerinizin UI mövzu ilə işlədilir və hər hansı bir uzun proses istifadəçi interfeyinə mane olur, bu da cavab vermir. Bundan əlavə, JavaScript üçün yuxarı işləmə həddi var və brauzer istifadəçidən icrasını davam etdirmək istəməyini istəməyəcəkdir.

Bütün bunlar həqiqətən pis bir istifadəçi təcrübəsi. İstifadəçi hər şey düzgün və ya düzgün işləmədiyini söyləyə bilməyəcək. Bundan əlavə, təsir yavaş bir əlaqəsi olan istifadəçilər üçün daha pis olacaqdır.

Bundan sonra, bir-birinin üstündə tikilmiş üç müxtəlif həllinə baxırıq:

  • Async async/await bekliyor (ES2017 +, bir daşıyıcı və ya rejenerator istifadə edərkən köhnə brauzerlərdə mövcuddur) ilə vəd edir
  • Çağırışlar (saytda məşhurdur)
  • then() (ES2015 +, çox vəd kitabxanalarından birini istifadə edərkən köhnə brauzerlərdə mövcuddur)

Bütün üçü mövcud brauzerlər və 7 + node şəbəkələrində mövcuddur.


ES2017 +: async ilə vəd async/await bekliyor

2017-ci ildə çıxarılan ECMAScript versiyası asinxron funksiyalar üçün sintaksis dəstəyi təqdim etdi. asyncawait "sinxron şəkildə" asynchronously yaza bilərsiniz. Kod hələ asinxron deyil, ancaq oxumaq / anlamaq üçün asandır.

async/await vədlərə əsaslanır: async funksiyası həmişə bir söz verir. sözü "açılmır" və ya sözün mənası ilə nəticə nəticə vermədi və ya söz rad edildiyi təqdirdə səhv verir.

Vacib: Yalnız async funksiyası daxilində async . Hal-hazırda, ən yüksək səviyyədə, gözləyin hələ dəstəklənmir, belə ki, asynchronous IIFE async kontekstinə başlamaq lazımdır.

async haqqında daha ətraflı oxuya bilər və MDN-ni await .

Burada yuxarıda göstərilən gecikməyə əsaslanan bir nümunədir:

 // 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 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; } } // Start an IIFE to use 'await' at the top level (async function(){ let books = await getAllBooks(); console.log(books); })(); 

Brauzerinhostun hazırkı versiyaları async/await . Kodunuzu ES5'e bir rejenerator (və ya Babel kimi bir rejenerator istifadə edən vasitələrdən istifadə) vasitəsilə çevirməklə köhnə mühitləri saxlaya bilərsiniz.


Funksiyalar çağırışları qəbul edilsin.

Geri çağırış sadəcə başqa funksiyaya ötürülən funksiyadır. Bu digər funksiya hazır olduqda keçən funksiyanı çağırır. Asynchronous bir proses kontekstində asynchronous bir prosesin yerinə yetirildiyi zaman bir geri çağırılacaq. Normalda nəticə geri çağırışa göndərilir.

Sual nümunəsində, foo bir geri çağırışı qəbul və onu success geri çağırış kimi istifadə etməyə məcbur edə bilərsiniz. Beləliklə

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

olur

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

Burada "inline" funksiyasını müəyyən edirik, lakin funksiyaya istinad göstərə bilərsiniz:

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

foo özü aşağıdakı kimi müəyyən edilir:

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

callback biz onu çağırdığımız zaman foo keçmək funksiyasına istinad edəcək və sadəcə onu success keçiririk. Yəni Ajax sorğusu müvəffəqiyyətli $.ajax , $.ajax callback $.ajax müraciət edir və callback $.ajax cavab verəcəkdir (geri çağırışı necə təyin etdiyimizdən bu result ilə istinad edilə bilər).

Cavabınızı geri çağırmağa göndərmədən əvvəl də işlədə bilərsiniz:

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

Geri çağırışları istifadə edərək kodu yazma göründüyündən asandır. Bütün bunlardan sonra, brauzerdə olan JavaScript çox hadisələrə (DOM hadisələrinə) çox bağlıdır. Ajax cavabını almaq bir hadisədir.
Üçüncü tərəf kodu ilə işlədiyiniz zaman çətinliklər yarana bilər, amma problemlərin əksəriyyəti sadəcə tətbiqi axını ilə düşünməklə həll edilə bilər.


ES2015 +: sonra vədlərlə ()

Promise API , ECMAScript 6 (ES2015) yeni bir xüsusiyyətdir, lakin artıq yaxşı brauzer dəstəyi var . Standart API Təklifləri həyata keçirən və asinxron funksiyaları (məsələn, bluebird ) istifadə və tərkibini asanlaşdırmaq üçün əlavə üsullar təmin edən bir çox kitabxana da var.

Promises gələcək dəyərlər üçün konteynerlərdir. Bir söz dəyəri (icazə verilir) və ya ləğv edildikdə (rədd edildikdə), bu dəyəri əldə etmək istəyən bütün "dinləyicilərini" bildirir.

Sadə geri çağırışlar üstünlüyü, kodunuzu ayırmaq üçün imkan verir və onları tərtib etmək daha asandır.

Söz vermək üçün sadə bir nümunə:

 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 zənginə gəldikdə, aşağıdakı vədlərdən istifadə edə bilərik:

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

Təklif verdikləri bütün faydaların təsviri bu cavabın əhatəsindən kənarda, lakin yeni bir kod yazarkən onları ciddi şəkildə nəzərdən keçirməliyik. Kodunuzun əla soyudulması və ayrılmasını təmin edirlər.

Promises haqqında daha ətraflı məlumat: HTML5 - qayalar - JavaScript vəd edir

Qeyd: jQuery gözləyən obyektlər

Təxirə salınmış obyektlər jQuery-də vədlərin xüsusi həyata keçirilməsidir (Promise API standartlaşdırılmasından əvvəl). Onlar deyirlər kimi demək olar ki, davranırlar, amma bir az fərqli API ortaya qoyurlar.

Hər bir jQuery Ajax üsulu artıq "gözlənilən obyekt" (əslində gözləməyən bir obyektin verdiyi bir sözü) ilə qaytarır və siz sadəcə funksiyasından dönə bilərsiniz:

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

Qeyd: söz çıxdı

Unutmayın ki, vədlər və təxirə salınmış obyektlər dəyərin özü deyil, gələcək dəyər üçün konteynerlərdir. Məsələn, şübhə etdiniz:

 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 } 

Bu kod yuxarıdakı asinxron problemləri səhv edir. Xüsusilə $.ajax() '/ parol' səhifəsini yoxlamaq zamanı $.ajax() - serverə bir sorğu göndərir və gözləyərkən jQuery Ajax təxirə salınan obyekti serverdən cavab deyil, dərhal qaytarır. Bu, if həmişə bu təxirə salınmış obyekti qəbul edərsə, true olaraq true və istifadəçi daxil olduğu kimi hərəkət edir. Yaxşı deyil

Amma asanlıqla düzəldin:

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

Tövsiyə edilmir: sinxron zənglər "Ajax"

Dediyim kimi, bəzi (!) Asynchronous əməliyyatlar sinxron tərəfdaşları var. Mən onların istifadəsini müdafiə etmirəm, amma tamlığı naminə, sinxron çağırışınızı necə yerinə yetirməliyəm:

Xeyr jQuery

XMLHTTPRequest obyektini birbaşa istifadə XMLHTTPRequest , üçüncü arqument kimi false XMLHTTPRequest .

Jquery

JQuery istifadə edirsinizsə, async parametrini false olaraq təyin edə bilərsiniz. Xahiş edirik unutmayın ki, bu seçim jQuery 1.8-dən bəri tətbiq edilmir. Sonra da success geri çağırma və ya jqXHR obyektinin responseText xüsusiyyətindən istifadə edə bilərsiniz:

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

$.getJSON , $.getJSON , və s. Kimi başqa hər hansı bir jQuery Ajax metodunu istifadə $.getJSON , onu $.ajax (yalnız $.ajax parametrlərinə konfiqurasiya parametrlərindən keçə $.ajax ) $.ajax .

Diqqət edin Sinxron JSONP tələbi edə bilmir. JSONP həmişə asinxron xarakterlidir (hətta bu seçimi nəzərə almamaq üçün bir səbəbdir).

4957
08 янв. Cavab Felix Kling Jan 08 tərəfindən verilir 2013-01-08 20:06 '13 at 20:06 2013-01-08 20:06

Kodunuzda jQuery istifadə etmirsinizsə, bu cavab sizin üçündür.

Kodunuz belə olmalıdır:

 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 AJAX üçün jQuery istifadə edən insanlar üçün cavab yazan böyük bir iş etdi, mən etməyən insanlar üçün bir alternativ təmin etmək qərarına gəldim.

( Qeyd edək ki, yeni API fetch , açmaq və ya vədlər istifadə edənlər üçün aşağıdakı bir cavab əlavə etdim )


Nə qarşılaşırsınız?

Bu, "Cavabın izahı" nın digər cavabından qısa bir xülasəsidir, əgər əmin deyilsinizsə, bunu oxuduqdan sonra oxuyun.

A ilə AJAX asynchronous deməkdir. Demək ki, bir sorğu göndərmək (və ya əksinə cavab almaq) normal axından çıxarılır. Sizin nümunənizdə dərhal geri qaytarılır və növbəti bəyanat return result; success çağırışı olaraq keçdiyiniz funksiyadan əvvəl də icra edilirdi.

Bu, qayıtdığınız zaman, göstərdiyiniz dinləyici hələ yerinə yetirilməməsi deməkdir, yəni siz qaytardığınız dəyər müəyyənləşdirilməyib.

Sadə analogiya

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

(Feeddle)

a=5 nin yerinə yetirilmədiyi üçün a ın qaytarılması dəyəri undefined . AJAX bunu edir, server dəyəri brauzerinizə nə demək istədikdən əvvəl dəyəri qaytarırsınız.

Bu problemin mümkün həlli kodun yenidən istifadə edilməsi, proqramın hesablanması başa çatdıqda nə edəcəyi barədə məlumatlandırmaqdır.

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

Bu CPS adlanır. Əsasən, biz getFive hərəkətləri verdikdə, hadisəni bitdikdə necə reaksiya verəcəyimizi söyləyirik (məsələn, bizim AJAX çağırışı və ya bu halda bir zaman aşımı).

İstifadə edin:

 getFive(onComplete); 

Hansı ekranda "5" xəbərdarlıq etməlidir? (Fiddle) .

Mümkün çözümlər

border=0

Bu problemi həll etmək üçün əsasən iki yol var:

  • Bir AJAX zəngini sinxronlaşdırın (ona SJAX deyin).
  • Kodunuzu geri çağırışlarla düzgün işləmək üçün yenidən qurun.

1. Sinxron AJAX - bunu etməyin!

Sinxron AJAX-ə gəldikdə, bunu etməyin! Feliksın cavabı bunun pis bir fikir olduğuna dair bəzi güclü mübahisələr verir. Xülasə etmək üçün, server bir cavab qaytarır və çox pis istifadəçi interfeysi yaradana qədər istifadəçinin brauzerini dondurur. Nə üçün digər MDN xülasəsi:

XMLHttpRequest həm sinxron, həm asynchronous ünsiyyəti dəstəkləyir. Ümumiyyətlə, performansın səbəbləri üçün sinxron istəklər üçün asynchronous müraciətlər üstünlük təşkil etməlidir.

Bir sözlə, sinxron istəklər kodun icrasını bloklaşdırır ...... bu ciddi problemlərə səbəb ola bilər ...

Bunu etmək lazımdırsa, bayraqdan keçə bilərsiniz: İşdə necə:

 var request = new XMLHttpRequest(); request.open('GET', 'yourURL', false); // `false` makes the request synchronous request.send(null); if (request.status === 200) {// That HTTP for 'ok' console.log(request.responseText); } 

2. Yenidən Qurulması Kod

Funksiyanız geri çağırışı qəbul etsin. Məsələn, foo kodu geri çağırışı qəbul etmək üçün edilə bilər. foo başa çatdıqda reaksiya vermək üçün kodlarımızı izah edəcəyik.

Beləliklə:

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

olur:

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

Burada anonim bir funksiyadan keçdik, ancaq mövcud bir funksiyaya keçid verə bilərik:

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

Bu geri çağırış növünün necə yerinə yetirilməsi haqqında daha ətraflı məlumat üçün Felix cavabını yoxlayın.

İndi fooun özünə uyğun hərəkət etməsini təyin edək

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

(skripka)

İndi bizim foo funksiyası AJAX uğurla başa çatdıqdan sonra başlayan bir hərəkət qəbul edir, cavab vəziyyətinin 200 cavab verdiyini və müvafiq olaraq fəaliyyət göstərdiyini yoxlayaraq (hata işləyicisi yaratmaq və s.) Yarada bilərik. Problemimizə təsirli bir həll.

Bunu hələ başa düşmürsənsə, MDN- də AJAX Başlama Kılavuzu oxuyun .

939
30 мая '13 в 2:30 2013-05-30 02:30 Benjamin Gruenbaum tərəfindən cavab 30 May '13, 2:30 'da 2013-05-30 02:30

XMLHttpRequest 2 (Əvvəlcə Benjamin GrünbaumFelix Kling- dən cavab oxuyun)

Əgər jQuery istifadə etmirsinizsə və yeni brauzerlərdə, eləcə də mobil brauzerlərdə işləyən gözəl qısa bir XMLHttpRequest 2 əldə etmək istəmirəmsə, onu aşağıdakı kimi istifadə etməyi təklif edirəm:

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

Gördüyünüz kimi:

  1. Siyahıda göstərilən digər funksiyalardan daha qısadır.
  2. Geri çağırış birbaşa müəyyən edilir (buna görə də lazımsız lazımsız bağlanılar).
  3. XMLHttpRequest 1'yi canlandıranları xatırlamıram ki, bəzi digər hallar var.

Bu Ajax zənginə cavab almaq üçün iki yol var (üç XMLHttpRequest dəyişən adını istifadə edərək):

Ən sadə:

 this.response 

Və ya bir səbəblə bir siniflə geri bind()

 e.target.response 

Məsələn:

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

Yoxsa (yuxarıda göstərilən daha yaxşı anonim funksiyalar daim problemdir):

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

Heç bir şey asanlaşmır.

İndi bəzi insanlar yəqin ki, onreadystatechange və ya XMLHttpRequest dəyişəninin adını istifadə etmək daha yaxşıdır. Bu səhvdir.

XMLHttpRequest'in inkişaf etmiş xüsusiyyətlərini araşdırın

Bütün müasir brauzerlər dəstəklənir. Və mən bu yanaşmanı istifadə etdiyini təsdiqləyə bilərəm, çünki XMLHttpRequest 2. Mən istifadə etdiyim bütün brauzerlərdə heç bir problem yaşamadım.

onreadystatechange yalnız başlıqları 2-də əldə etmək istəyirsinizsə faydalıdır.

XMLHttpRequest dəyişən adını istifadə edərək başqa bir böyük səhvdir, çünki onload / oreadystatechange bağlanmalarında geri çağırmaq lazımdır, əks halda onu itirdik.


İndi poçt və FormData istifadə edərək daha mürəkkəb bir şey istəyirsinizsə, asanlıqla bu funksiyanı genişləndirə bilərsiniz:

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

Yenə ... Bu çox qısa bir funksiyadır, amma qəbul edir və dərc edir.

İstifadə nümunələri:

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

Və ya tam forma elementini ( document.getElementsByTagName('form')[0] ) keçin:

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

Və ya bəzi xüsusi dəyərlər qoyun:

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

Gördüyünüz kimi, sinxronizasiya tətbiq etmədi ... bu pisdir.

Bunu söyləyərək ... niyə sadə bir şəkildə bunu edə bilməzsən?


Şərhdə qeyd edildiyi kimi, səhv synchronous istifadə cavabı tamamilə cavabın mənasını pozur. Ajax'dan düzgün istifadə etmək üçün nə qədər yaxşı bir qısa yol var?

Hata işləyicisi

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

Yuxarıdakı senaryoda, statik olaraq tanımlanmış bir hata işleyiciniz var, bu funksiyanı uzlaşmır. Hata işləyicisi digər funksiyalar üçün istifadə edilə bilər.

Ancaq həqiqətən səhv almaq üçün yalnız yol səhv bir URL yazmaqdır, bu halda hər bir brauzer səhv verir.

Обработчики ошибок могут быть полезны, если вы устанавливаете пользовательские заголовки, устанавливаете responseType для буфера массива blob или чего-то еще...

Даже если вы передадите POSTAPAPAP в качестве метода, он не выдаст ошибку.

Даже если вы передадите 'fdggdgilfdghfldj' в качестве форм-данных, это не выдаст ошибку.

В первом случае ошибка находится внутри displayAjax() в this.statusText как Method not Allowed .

Во втором случае это просто работает. Вы должны проверить на стороне сервера, если вы передали правильные данные поста.

междоменный домен не разрешен, выдает ошибку автоматически.

В ответе об ошибке нет кодов ошибок.

Существует только this.type который имеет значение error.

Зачем добавлять обработчик ошибок, если вы полностью не можете контролировать ошибки? Большинство ошибок возвращаются внутри этого в функции обратного вызова displayAjax() .

Итак: нет необходимости в проверке ошибок, если вы можете правильно скопировать и вставить URL. ;)

PS: В качестве первого теста я написал x ('x', displayAjax)..., и он полностью получил ответ...??? Поэтому я проверил папку, в которой находится HTML, и там был файл с именем "x.xml". Так что даже если вы забудете расширение вашего файла, XMLHttpRequest 2 НАЙДЕТ ЕГО. Я смеюсь


Чтение файла синхронно

Не делай этого.

Если вы хотите на время заблокировать браузер, загрузите красивый большой .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'); 

Нет другого способа сделать это не асинхронно. (Да, с циклом setTimeout... но серьезно?)

Другой момент... если вы работаете с API-интерфейсами или просто со своими собственными файлами списков или какими-либо другими функциями, которые вы всегда используете для каждого запроса...

Только если у вас есть страница, где вы всегда загружаете один и тот же XML/JSON или что-то еще, вам нужна только одна функция. В этом случае немного измените функцию Ajax и замените b своей специальной функцией.


Функции выше предназначены для базового использования.

Если вы хотите расширить функцию...

Да, ты можешь.

Я использую множество API, и одной из первых функций, которые я интегрирую в каждую HTML-страницу, является первая функция Ajax в этом ответе, только с GET...

Но вы можете многое сделать с XMLHttpRequest 2:

Я сделал менеджер загрузок (используя диапазоны с обеих сторон с помощью резюме, файлового ридера, файловой системы), различные конвертеры, изменяющие размер изображения, используя холст, заполнял базы данных веб-SQL с помощью base64image и многое другое... Но в этих случаях вы должны создать функцию только для этого цель... иногда вам нужны блоб, буферы массивов, вы можете установить заголовки, переопределить mimetype и многое другое...

Но вопрос здесь в том, как вернуть ответ Ajax... (я добавил простой способ.)

333
ответ дан cocco 19 авг. '13 в 11:06 2013-08-19 11:06

Если вы используете promises, этот ответ для вас.

Это означает, что AngularJS, jQuery (с отсрочкой), собственная замена XHR (выборка), EmberJS, BackboneJS save или любая библиотека node, которая возвращает promises.

Ваш код должен быть чем-то вроде этого:

 function foo() { var data; // or $.get(...).then, or request(...).then, or query(...).then fetch("/echo/json").then(function(response){ data = response.json(); }); return data; } var result = foo(); // result is always undefined no matter what. 

Феликс Клинг отлично справился с написанием ответа для людей, использующих jQuery с обратными вызовами для AJAX. У меня есть ответ для родного XHR. Этот ответ предназначен для общего использования promises либо на интерфейсе, либо на бэкэнд.


Основная проблема

Модель JavaScript concurrency в браузере и на сервере с NodeJS/io.js является асинхронной и реактивной.

Всякий раз, когда вы вызываете метод, который возвращает обещание, обработчики then всегда выполняются асинхронно, то есть после ниже кода, который не находится в обработчике .then .

Это означает, что при возврате data обработчик then , который вы определили, еще не выполнил. Это, в свою очередь, означает, что возвращаемое вами значение не было установлено на правильное значение во времени.

Вот простая аналогия проблемы:

конструктор Promise для создания функции задержки: 

 function delay(ms){ // takes amount of milliseconds // returns a new promise return new Promise(function(resolve, reject){ setTimeout(function(){ // when the time is up resolve(); // change the promise to the fulfilled state }, ms); }); } 

Теперь, после преобразования setTimeout в использование promises, мы можем использовать then , чтобы он подсчитал:

268
ответ дан Benjamin Gruenbaum 12 мая '15 в 5:22 2015-05-12 05:22

Вы неправильно используете Ajax. Идея состоит в том, чтобы не возвращать что-либо, а вместо этого передавать данные на вызов, называемый функцией обратного вызова, которая обрабатывает данные.

Yəni:

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

Возвращение чего-либо в обработчике отправки ничего не сделает. Вы должны либо передать данные, либо делать то, что хотите, непосредственно внутри функции успеха.

209
ответ дан Nic 23 мая '14 в 5:05 2014-05-23 05:05

Самое простое решение - создать функцию JavaScript и вызвать его для обратного вызова Ajax success .

 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); }); 
199
ответ дан Hemant Bavle 18 февр. '14 в 21:58 2014-02-18 21:58

Я отвечу ужасным, нарисованным рукой комиком. Второе изображение является причиной того, что result undefined в вашем примере кода.

170
ответ дан Johannes Fahrenkrug 11 авг. '16 в 17:17 2016-08-11 17:17

Angular1

Для людей, которые используют AngularJS , можно справиться с этой ситуацией, используя Promises .

Здесь говорится:

Promises может использоваться для отключения асинхронных функций и позволяет объединять несколько функций вместе.

Здесь вы можете найти приятное объяснение здесь .

Пример, найденный в docs , указанный ниже.

  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 просмотрите следующий пример, но его рекомендуется использовать Observables с Angular2 .

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

}

Вы можете использовать это таким образом,

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

Смотрите оригинал здесь. Но Typescript не поддерживает native es6 Promises , если вы хотите его использовать, для этого вам может понадобиться плагин.

Кроме того, здесь описывается promises spec .

125
ответ дан Maleen Abewardana 26 авг. '14 в 11:11 2014-08-26 11:11

Большинство @ здесь дают полезные советы, когда у вас есть одна операция async, но иногда это возникает, когда вам нужно выполнить асинхронную операцию для каждой записи в массиве или в другой структуре, подобной списку. Искушение состоит в том, чтобы сделать это:

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

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

Причина, по которой не работает, заключается в том, что обратные вызовы из doSomethingAsync еще не запущены к моменту, когда вы пытаетесь использовать результаты.

Итак, если у вас есть массив (или список какого-то типа) и вы хотите выполнять асинхронные операции для каждой записи, у вас есть два варианта: выполняйте операции параллельно (перекрывающиеся) или последовательно (последовательно один за другим).

Parallel

Вы можете запустить все из них и отслеживать количество обратных вызовов, которые вы ожидаете, а затем использовать результаты, когда вы получили много обратных вызовов:

 var results = []; var expecting = theArray.length; theArray.forEach(function(entry, index) { doSomethingAsync(entry, function(result) { results[index] = result; if (--expecting === 0) { // Done! console.log("Results:", results); // Eg, using the results } }); }); 

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

(Мы могли бы покончить с expecting и просто использовать results.length === theArray.length , но это оставляет нам возможность возможности изменения theArray во время выдачи вызовов...)

Обратите внимание, как мы используем index из forEach , чтобы сохранить результат в results в той же позиции, что и запись, к которой он относится, даже если результаты не соответствуют порядку (так как асинхронные вызовы необязательно завершены в том порядке, в котором они были запущены).

Но что, если вам нужно вернуть эти результаты из функции? Как указывали другие ответы, вы не можете; вы должны принять вашу функцию и вызвать обратный вызов (или вернуть Promise ). Здесь обратная версия:

 function doSomethingWith(theArray, callback) { var results = []; var expecting = theArray.length; theArray.forEach(function(entry, index) { doSomethingAsync(entry, function(result) { results[index] = result; if (--expecting === 0) { // Done! callback(results); } }); }); } doSomethingWith(theArray, function(results) { console.log("Results:", results); }); 

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

Или здесь версия, возвращающая Promise вместо:

 function doSomethingWith(theArray) { return new Promise(function(resolve) { var results = []; var expecting = theArray.length; theArray.forEach(function(entry, index) { doSomethingAsync(entry, function(result) { results[index] = result; if (--expecting === 0) { // Done! resolve(results); } }); }); }); } doSomethingWith(theArray).then(function(results) { console.log("Results:", results); }); 

Конечно, если doSomethingAsync передал нам ошибки, мы использовали бы reject , чтобы отклонить обещание, когда мы получили сообщение об ошибке.)

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

(Или поочередно, вы можете сделать обертку для doSomethingAsync , которая возвращает обещание, а затем сделайте следующее...)

Если doSomethingAsync дает вам Promise , вы можете использовать Promise.all :

 function doSomethingWith(theArray) { return Promise.all(theArray.map(function(entry) { return doSomethingAsync(entry, function(result) { results.push(result); }); })); } doSomethingWith(theArray).then(function(results) { console.log("Results:", results); }); 

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

Обратите внимание, что Promise.all разрешает свое обещание с массивом результатов всех promises, которые вы даете ему, когда все они разрешены, или отклоняет свое обещание, когда первый из promises вы его отклоняете.

Серия

Предположим, вы не хотите, чтобы операции были параллельны? Если вы хотите запускать их один за другим, вам нужно дождаться завершения каждой операции, прежде чем вы начнете следующую. Вот пример функции, которая выполняет это и вызывает обратный вызов с результатом:

 function doSomethingWith(theArray, callback) { var results = []; doOne(0); function doOne(index) { if (index < theArray.length) { doSomethingAsync(theArray[index], function(result) { results.push(result); doOne(index + 1); }); } else { // Done! callback(results); } } } doSomethingWith(theArray, function(results) { console.log("Results:", results); }); 

(Поскольку мы выполняем работу последовательно, мы можем просто использовать results.push(result) , поскольку мы знаем, что мы не получим результаты не в порядке. В приведенном выше примере мы могли бы использовать results[index] = result; , но в некоторых из в следующих примерах мы не используем индекс.)

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

(Или, опять же, создайте обертку для doSomethingAsync , которая даст вам обещание и сделайте следующее...)

Если doSomethingAsync дает вам обещание, если вы можете использовать синтаксис ES2017 + (возможно, с транспилером, например Babel ), вы можете используйте async функцию с for-of и await :

 async function doSomethingWith(theArray) { const results = []; for (const entry of theArray) { results.push(await doSomethingAsync(entry)); } return results; } doSomethingWith(theArray).then(results => { console.log("Results:", results); }); 

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

Если вы не можете использовать синтаксис ES2017 + (пока), вы можете использовать вариацию в шаблоне Promise reduce (это сложнее обычного Promise уменьшить, потому что мы не передаем результат от одного к другому, а собираем результаты в массиве):

 function doSomethingWith(theArray) { return theArray.reduce(function(p, entry) { return p.then(function(results) { return doSomethingAsync(entry).then(function(result) { results.push(result); return results; }); }); }, Promise.resolve([])); } doSomethingWith(theArray).then(function(results) { console.log("Results:", results); }); 

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 

... что менее громоздко с ES2015 + функции стрелок :

 function doSomethingWith(theArray) { return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => { results.push(result); return results; })), Promise.resolve([])); } doSomethingWith(theArray).then(results => { console.log("Results:", results); }); 

Məsələn:

 .as-console-wrapper { max-height: 100% !important; } 
102
ответ дан TJ Crowder 03 мая '17 в 19:59 2017-05-03 19:59

Посмотрите на этот пример:

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

Как видите, getJoke возвращает разрешенное обещание (оно разрешается при возврате res.data.value ). Поэтому вы ждете, пока запрос $ http.get не будет выполнен, а затем будет выполнен console.log(res.joke) (как обычный асинхронный поток).

Это плнкр:

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

ES6 способ (асинхронно - жду)

 (function(){ async function getJoke(){ let response = await fetch('http://api.icndb.com/jokes/random'); let data = await response.json(); return data.value; } getJoke().then((joke) => { console.log(joke); }); })(); 
84
ответ дан Francisco Carmona 02 июня '16 в 11:31 2016-06-02 11:31

Другой подход к возврату значения из асинхронной функции - передать объект, который сохранит результат от асинхронной функции.

Вот пример того же:

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

Я использую объект result для хранения значения во время асинхронной операции. Это позволяет получить результат даже после асинхронного задания.

Я использую этот подход много. Мне было бы интересно узнать, насколько хорошо этот подход работает, когда задействован результат обратно через последовательные модули.

74
ответ дан jsbisht 02 сент. '15 в 15:54 2015-09-02 15:54

Это одно из мест, в котором два способа связывания данных, которые использовались во многих новых фреймворках JavaScript, будут очень полезны для вас...

Поэтому, если вы используете Angular, React или любые другие фреймворки, которые выполняют два способа привязки данных, эта проблема просто исправлена для вас, поэтому простым языком ваш результат не undefined на первом этапе, так что вы получите result = undefined перед вами получите данные, а затем, как только вы получите результат, он будет обновлен и получит назначение на новое значение, которое отвечает на ваш вызов Ajax...

Но как вы можете это сделать в чистом javascript или jQuery, например, как вы задавали этот вопрос?

Вы можете использовать обратный вызов , обещание и недавно наблюдаемое, чтобы обрабатывать его для вас, например, в обещаниях мы имеем некоторые функции, такие как success() или then(), которые будут выполняться, когда ваши данные готовы для вас, так же, как функция обратного вызова или подписки по наблюдаемым .

Например, в вашем случае, в котором вы используете jQuery , вы можете сделать что-то вроде этого:

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

Для получения дополнительной информации изучите обещания и наблюдаемые, которые являются более новыми способами сделать эту асинхронную работу.

68
ответ дан Alireza 24 мая '17 в 12:38 2017-05-24 12:38

В то время как promises и обратные вызовы работают нормально во многих ситуациях, боль в задней части выражает что-то вроде:

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

В итоге вы пройдете async1 ; проверьте, есть ли name undefined или нет, и соответственно вызовите обратный вызов.

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

Хотя в небольших примерах это нормально, это вызывает раздражение, когда у вас много подобных случаев и обработка ошибок.

Fibers помогает в решении проблемы.

 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 } 

Вы можете проверить проект здесь .

65
ответ дан rohithpr 25 янв. '16 в 20:43 2016-01-25 20:43

Короткий ответ: вам нужно выполнить обратный вызов следующим образом:

 function callback(response) { // Here you can do what ever you want with the response object. console.log(response); } $.ajax({ url: "...", success: callback }); 
62
ответ дан Pablo Matias Gomez 22 апр. '16 в 17:47 2016-04-22 17:47

Следующий пример, который я написал, показывает, как

  • Обработка асинхронных HTTP-вызовов;
  • Подождите ответа от каждого вызова API;
  • Используйте шаблон Promise ;
  • Используйте шаблон Promise.all для объединения нескольких HTTP-вызовов;

Этот рабочий пример является самодостаточным. Он будет определять простой объект запроса, который использует объект XMLHttpRequest для выполнения вызовов. Он определит простую функцию, чтобы дождаться завершения кучи обещаний.

Контекст. В этом примере запрашивается конечная точка Spotify Web API для поиска объектов playlist для заданного набора строк запроса:

 [ "search?type=playlist "search?type=playlist ] 

Для каждого элемента новая Promise будет запускать блок - ExecutionBlock , анализировать результат, планировать новый набор обещаний на основе массива результатов, который является списком объектов user Spotify и выполнять асинхронный новый HTTP-вызов в ExecutionProfileBlock .

Затем вы можете увидеть вложенную структуру Promise, которая позволяет создавать множественные и полностью асинхронные вложенные HTTP-вызовы и присоединяться к результатам от каждого поднабора вызовов через Promise.all .

Qeyd. Новые API-интерфейсы Spotify для search должны указывать маркер доступа в заголовках запроса:

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

Итак, для запуска следующего примера вам нужно поместить свой токен доступа в заголовки запроса:

 <div id="console" /> 

Я подробно обсуждал это решение здесь .

58
ответ дан loretoparisi 13 апр. '16 в 1:55 2016-04-13 01:55

2017 ответ: теперь вы можете делать то, что хотите, в каждом текущем браузере и узле

Это довольно просто:

  • Возвращение обещания
  • Используйте "ожидание" , которое скажет JavaScript, ожидая, что обещание будет разрешено в значение (например, ответ HTTP)
  • Добавьте ключевое слово "async" в родительскую функцию

Вот рабочая версия вашего кода:

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

ожидание поддерживается во всех текущих браузерах и узле 8

55
ответ дан mikemaccana 02 июня '17 в 12:51 2017-06-02 12:51

Вы можете использовать эту пользовательскую библиотеку (написанную с помощью Promise) для выполнения удаленного вызова.

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

Простой пример использования:

 $http({ method: 'get', url: 'google.com' }).then(function(response) { console.log(response); }, function(error) { console.log(error) }); 
50
ответ дан Vinoth Rajendran 26 мая '16 в 16:26 2016-05-26 16:26

Js - однопоточная.

Браузер можно разделить на три части:

1) Цикл событий

2) Веб-API

3) Очередь событий

Event Loop запускается вечно, т.е. представляет собой бесконечный цикл. Queue - это когда вся ваша функция нажата на какое-либо событие (пример: нажмите), это один за другим выполняется из очереди и помещается в цикл событий, которые выполняют эту функцию и подготавливает его самостоятельно для следующего после первого запуска. Это означает, что выполнение одной функции не запускается до тех пор, пока функция перед ней в очереди не будет выполнена в цикле событий.

Теперь давайте подумаем, что мы поставили две функции в очереди: для получения данных с сервера, а другой использует эти данные. Сначала мы нажали функцию serverRequest() в очереди, а затем применили функцию Data(). Функция serverRequest входит в цикл событий и делает вызов серверу, поскольку мы никогда не знаем, сколько времени потребуется для получения данных с сервера поэтому ожидается, что этот процесс займет много времени, и поэтому мы заняли наш цикл событий, таким образом, висящий на нашей странице, что, когда веб-API входит в роль, он принимает эту функцию из цикла событий и имеет дело с сервером, создающим цикл событий, чтобы мы могли выполнять следующую функцию из queue. Следующая функция в очереди - useData(), которая идет в цикле, но из-за отсутствия доступных данных это идет впустую, а выполнение следующей функции продолжается до конца очереди. (Это называется Async-вызовом, то есть мы можем сделать что-то еще, пока мы получить данные)

Предположим, что наша функция serverRequest() имела оператор возврата в коде, когда мы возвращаем данные с сервера. Web API будет выталкивать его в очередь в конце очереди. Поскольку он оказывается в очереди в очереди, мы не можем использовать его данные, поскольку в нашей очереди нет функции, чтобы использовать эти данные. Таким образом, невозможно вернуть что-то из Async Call.

Таким образом, решение этого - обратный вызов или обещание.

A Изображение из одного из @ здесь, правильно объясняет использование обратного вызова... Мы предоставляем нашу функцию (функцию, использующую данные, возвращенные с сервера), чтобы вызвать вызывающий сервер.

2019

ответ дан Aniket Jha 03 февр. '18 в 9:06 2018-02-03 09:06

Другим решением является выполнение кода через последовательный исполнитель nsynjs .

Если основная функция обеимитирована

nsynjs будет последовательно оценивать все promises и поместить результат результата в свойство data :

 <script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script> 

Если базовая функция не является многообещающей

Шаг 1. Функция Wrap с обратным вызовом в оболочку, совместимую с nsynjs (если у нее есть обещанная версия, вы можете пропустить этот тест):

 var ajaxGet = function (ctx,url) { var res = {}; var ex; $.ajax(url) .done(function (data) { res.data = data; }) .fail(function(e) { ex = e; }) .always(function() { ctx.resume(ex); }); return res; }; ajaxGet.nsynjsHasCallback = true; 

Шаг 2. Вставьте синхронную логику в функцию:

 function process() { console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data); } 

Шаг 3. Запустите функцию синхронно через nnsynjs:

 nsynjs.run(process,this,function () { console.log("synchronous function finished"); }); 

Nsynjs будет оценивать все операторы и выражения шаг за шагом, приостанавливая выполнение в случае, если результат некоторой медленной функции не готов.

Другие примеры здесь: https://github.com/amaksr/nsynjs/tree/master/examples

45
ответ дан amaksr 27 мая '17 в 5:47 2017-05-27 05:47

Это очень распространенная проблема, с которой мы сталкиваемся, борясь с "тайн" JavaScript. Позвольте мне попробовать демистифицировать эту тайну сегодня.

Начнем с простой функции JavaScript:

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

Это простой синхронный вызов функции (где каждая строка кода "завершена с заданием" до следующего в последовательности), и результат будет таким же, как и ожидалось.

Теперь добавьте немного поворота, введя небольшую задержку в нашей функции, чтобы все строки кода не были "закончены" последовательно. Таким образом, он будет эмулировать асинхронное поведение функции:

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

Итак, вы идете, эта задержка просто сломала функциональность, которую мы ожидали! Но что именно произошло? Ну, это на самом деле довольно логично, если вы посмотрите на код. функция foo() после выполнения ничего не возвращает (при этом возвращаемое значение не undefined ), но он запускает таймер, который выполняет функцию после 1s, чтобы вернуть "wohoo". Но, как вы можете видеть, значение, присвоенное бару, является немедленно возвращенным материалом из foo(), а не что-либо еще, что приходит позже.

Итак, как мы решаем эту проблему?

Давайте попросим нашу функцию для ОБЕЩАНИЯ . Обещание действительно о том, что это означает: это означает, что функция гарантирует, что вы предоставите любой результат, который он получит в будущем. так что посмотрим на это в нашей маленькой проблеме выше:

 function foo(){ return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something setTimeout ( function(){ // promise is RESOLVED , when execution reaches this line of code 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' }); 

Таким образом, резюме - для решения асинхронных функций, таких как вызовы на основе ajax и т.д., Вы можете использовать обещание для resolve значения (которое вы собираетесь вернуть). Таким образом, короче говоря, вы разрешаете значение вместо возврата в асинхронных функциях.

ОБНОВЛЕНИЕ (Обещает с асинхронным/ожиданием)

Помимо использования then/catch для работы с обещаниями, существует еще один подход. Идея состоит в том, чтобы распознать асинхронную функцию, а затем ждать выполнения обещаний, прежде чем перейти к следующей строке кода. Это все еще просто promises под капотом, но с другим синтаксическим подходом. Чтобы сделать все более ясным, вы можете найти сравнение ниже:

затем /catch версия:

 function fetchUsers(){ let users = []; getUsers() .then(_users => users = _users) .catch(err =>{ throw err }) return users; } 

Версия async/await:

  async function fetchUsers(){ try{ let users = await getUsers() return users; } catch(err){ throw err; } } 
35
ответ дан Anish K. 31 окт. '17 в 23:12 2017-10-31 23:12

Ниже приведены некоторые подходы к работе с асинхронными запросами:

Пример: jQuery отложенная реализация для работы с несколькими запросами

31
ответ дан Mohan Dere 13 авг. '16 в 12:36 2016-08-13 12:36

ECMAScript 6 имеет "генераторы", которые позволяют легко программировать в асинхронном стиле.

 function* myGenerator() { const callback = yield; let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback}); console.log("response is:", response); // examples of other things you can do yield setTimeout(callback, 1000); console.log("it delayed for 1000ms"); while (response.statusText === "error") { [response] = yield* anotherGenerator(); } } 

Для запуска вышеуказанного кода вы выполните следующее:

 const gen = myGenerator(); // Create generator gen.next(); // Start it gen.next((...args) => gen.next([...args])); // Set its callback function 

Если вам нужно настроить таргетинг на браузеры, которые не поддерживают ES6, вы можете запустить код через Babel или make-compiler для генерации ECMAScript 5.

Обратные вызовы ...args завернуты в массив и деструктурированы, когда вы их читаете, чтобы шаблон мог справиться с обратными вызовами, имеющими несколько аргументов. Например, с узлом fs :

 const [err, data] = yield fs.readFile(filePath, "utf-8", callback); 
30
ответ дан James 17 февр. '18 в 18:26 2018-02-17 18:26

Используйте функцию callback() внутри успеха foo() . Попробуйте таким образом. Это просто и легко понять.

 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(); 
28
ответ дан Mahfuzur Rahman 24 апр. '17 в 11:09 2017-04-24 11:09

Короткий ответ . Ваш метод foo() возвращается немедленно, а вызов $ajax() выполняется асинхронно после возвращения функции. Проблема заключается в том, как и где хранить результаты, полученные при вызове async, после его возврата.

В этой цепочке даны несколько решений. Возможно, самый простой способ - передать объект методу foo() и сохранить результаты в члене этого объекта после завершения асинхронного вызова.

 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 

Обратите внимание, что вызов foo() по-прежнему не возвращает ничего полезного. Однако результат асинхронного вызова теперь будет сохранен в result.response .

26
ответ дан David R Tribble 24 сент. '15 в 1:52 2015-09-24 01:52

Вопрос был:

Как вернуть ответ от асинхронного вызова?

который МОЖЕТ интерпретироваться как:

Как сделать асинхронный код синхронным ?

Решение будет состоять в том, чтобы избежать обратных вызовов и использовать комбинацию Promises и async/wait .

Я хотел бы привести пример для запроса Ajax.

(Хотя он может быть написан в Javascript, я предпочитаю писать его на Python и компилировать его в Javascript с помощью Transcrypt . Это будет достаточно ясно.)

Позволяет сначала включить использование JQuery, иметь $ доступный как S :

 __pragma__ ('alias', 'S', '$') 

Определите функцию, которая возвращает Promise , в этом случае вызов Ajax:

 def read(url: str): deferred = S.Deferred() S.ajax({'type': "POST", 'url': url, 'data': { }, 'success': lambda d: deferred.resolve(d), 'error': lambda e: deferred.reject(e) }) return deferred.promise() 

Используйте асинхронный код, как если бы он был синхронным :

 async def readALot(): try: result1 = await read("url_1") result2 = await read("url_2") except Exception: console.warn("Reading a lot failed") 
20
ответ дан Pieter Jan Bonestroo 13 янв. '18 в 22:13 2018-01-13 22:13

Конечно, существует много подходов, таких как синхронный запрос, обещание, но из моего опыта я думаю, что вы должны использовать подход обратного вызова. Это естественно для асинхронного поведения Javascript. Итак, ваш фрагмент кода можно переписать немного иначе:

 function foo() { var result; $.ajax({ url: '...', success: function(response) { myCallback(response); } }); return result; } function myCallback(response) { // Does something. } 
20
ответ дан Khoa Bui 05 июля '17 в 23:28 2017-07-05 23:28

Мы попадаем во вселенную, которая, кажется, движется в измерении, которое мы называем "время". Мы не очень понимаем, что такое время, но мы разработали абстракции и словарный запас, которые позволяют нам рассуждать и говорить об этом: "прошлое", "настоящее", "будущее", "до", "после".

Компьютерные системы, которые мы создаем - все больше и больше - имеют время как важное измерение. Определенные вещи созданы, чтобы произойти в будущем. Затем должны произойти другие вещи после того, как эти первые вещи в конечном итоге произойдут Это основное понятие называется "асинхронность". В нашем мире, который становится все более сетевым, наиболее распространенный случай асинхронности - ожидание того, что удаленная система ответит на какой-либо запрос.

Рассмотрим пример. Вы звоните молочнику и заказываете молоко. Когда это приходит, вы хотите положить его в свой кофе. Вы не можете положить молоко в свой кофе прямо сейчас, потому что его еще нет. Вы должны ждать, пока оно придет, прежде чем положить его в свой кофе. Другими словами, следующее не будет работать:

 var milk = order_milk(); put_in_coffee(milk); 

Потому что у JS нет возможности узнать, что ему нужно дождаться order_milk прежде чем он выполнит put_in_coffee . Другими словами, он не знает, что order_milk является асинхронным --is, что не приведет к появлению молока до некоторого будущего времени. JS и другие декларативные языки выполняют один оператор за другим без ожидания.

Классический подход JS к этой проблеме, использующий преимущество того факта, что JS поддерживает функции как объекты первого класса, которые могут быть переданы, состоит в том, чтобы передать функцию в качестве параметра асинхронному запросу, который он затем вызовет после завершения. его задача когда-нибудь в будущем. Это подход "обратного вызова". Это выглядит так:

 order_milk(put_in_coffee); 

order_milk , заказывает молоко, затем, когда и только когда оно прибывает, оно вызывает put_in_coffee .

Проблема с этим подходом обратного вызова состоит в том, что он загрязняет нормальную семантику функции, сообщающей о своем результате с return ; вместо этого функции не должны сообщать о своих результатах, вызывая функцию обратного вызова, заданную в качестве параметра. Кроме того, этот подход может быстро стать громоздким при работе с более длинными последовательностями событий. Например, допустим, что я хочу подождать, пока молоко будет добавлено в кофе, а затем и только тогда выполнить третий шаг, а именно выпить кофе. В итоге мне нужно написать что-то вроде этого:

 order_milk(function(milk) { put_in_coffee(milk, drink_coffee); } 

где я передаю put_in_coffee и молоко, которое нужно вставить в него, и действие ( drink_coffee ), которое нужно выполнить после того, как молоко было введено. Такой код становится трудным для написания, чтения и отладки.

В этом случае мы могли бы переписать код в вопросе как:

 var answer; $.ajax('/foo.json') . done(function(response) { callback(response.data); }); function callback(data) { console.log(data); } 

Введите обещания

Это послужило мотивацией для понятия "обещание", которое представляет собой особый тип значения, представляющий будущий или какой-то асинхронный результат. Он может представлять то, что уже произошло, или произойдет в будущем, или может никогда не произойти вообще. У обещаний есть единственный метод, названный then , которому вы передаете действие, которое будет выполнено, когда результат, который представляет обещание, был реализован.

В случае с нашим молоком и кофе мы проектируем order_milk чтобы он возвращал обещание на прибытие молока, а затем определяем put_in_coffee в качестве действия then , как put_in_coffee ниже:

 order_milk() . then(put_in_coffee) 

Одним из преимуществ этого является то, что мы можем связать их вместе, чтобы создать последовательности будущих вхождений ("цепочка"):

 order_milk() . then(put_in_coffee) . then(drink_coffee) 

Пусть применят обещания к вашей конкретной проблеме. Мы обернем нашу логику запроса внутри функции, которая возвращает обещание:

 function get_data() { return $.ajax('/foo.json'); } 

На самом деле все, что мы сделали, это добавили return к вызову $.ajax . Это работает, потому что jQuery $.ajax уже возвращает что-то вроде обещания. (На практике, не вдаваясь в подробности, мы бы предпочли обернуть этот вызов так, чтобы он возвращал реальное обещание, или использовать какую-то альтернативу $.ajax которая делает это.) Теперь, если мы хотим загрузить файл и дождаться его чтобы закончить, а затем сделать что-то, мы можем просто сказать,

 get_data() . then(do_something) 

məsələn

 get_data() . then(function(data) { console.log(data); }); 

При использовании обещания, мы в конечном итоге прохожу множество функций в then , так что часто бывает полезно использовать более компактные функции со стрелками ES6 стиля:

 get_data() . then(data => console.log(data)); 

async ключевое слово

Но все же есть что-то смутно неудовлетворенное тем, что нужно писать код одним способом, если он синхронный, и совершенно другим способом, если он асинхронный. Для синхронного мы пишем

 a(); b(); 

но если a асинхронный, с обещаниями мы должны написать

 a() . then(b); 

Выше мы говорили: "JS не может знать, что ему нужно дождаться завершения первого вызова, прежде чем он выполнит второй". Разве не было бы хорошо, если бы был какой-то способ сказать JS об этом? Оказывается, есть ключевое слово await , используемое внутри специального типа функции, называемой "асинхронной" функцией. Эта функция является частью будущей версии ES, но она уже доступна в таких транспортерах, как Babel, с правильными настройками. Это позволяет нам просто написать

 async function morning_routine() { var milk = await order_milk(); var coffee = await put_in_coffee(milk); await drink(coffee); } 

В вашем случае вы сможете написать что-то вроде

 async function foo() { data = await get_data(); console.log(data); } 
19
ответ дан user663031 23 янв. '16 в 6:28 2016-01-23 06:28

Используя ES2017, вы должны иметь это как объявление функции

 async function foo() { var response = await $.ajax({url: '...'}) return response; } 

И выполните его так.

 (async function() { try { var result = await foo() console.log(result) } catch (e) {} })() 

Или синтаксис Promise

 foo().then(response => { console.log(response) }).catch(error => { console.log(error) }) 
11
ответ дан Fernando Carvajal 24 янв. '18 в 9:18 2018-01-24 09:18

Сначала посмотрим на лес, прежде чем смотреть на деревья.

Есть много информативных @ с большими подробностями здесь, я не буду повторять ни одного из них. Ключ к программированию в JavaScript имеет сначала правильную ментальную модель общего исполнения.

  • Ваша точка входа (ов) выполняется в результате события. Для Например, тег script с кодом загружается в браузер. (Соответственно, поэтому вам может потребоваться готовность страницы запускать ваш код, если он требует элементов dom и т.д.)
  • Ваш код исполняется до завершения - однако многие асинхронные вызовы делает - без выполнения любых ваших обратных вызовов, включая XHR запросы, установить тайм-ауты, обработчики событий dom и т.д. Каждый из этих обратных вызовов, ожидающих исполнения, будет находиться в очереди, ожидая, что их очередь будет запущена после того, как другие запущенные события завершат выполнение.
  • Каждый индивидуальный ответ на запрос XHR, задание тайм-аута или dom событие после вызова будет завершено.

Хорошей новостью является то, что, если вы хорошо понимаете этот момент, вам никогда не придется беспокоиться о гоночных условиях. Прежде всего вы должны понимать, как вы хотите упорядочить свой код как по существу ответ на разные дискретные события, и как вы хотите объединить их в логическую последовательность. Вы можете использовать promises или более высокий уровень новых асинхронных/ожидающих в качестве инструментов с этой целью, или вы можете использовать их самостоятельно.

Но вы не должны использовать какие-либо тактические инструменты для решения проблемы до тех пор, пока вам не станет комфортно с реальным проблемным доменом. Нарисуйте карту этих зависимостей, чтобы знать, что нужно запускать, когда. Попытка ad-hoc подхода ко всем этим обратным вызовам просто не поможет вам хорошо.

10
ответ дан Haim Zamir 25 окт. '17 в 6:22 2017-10-25 06:22

Вместо того, чтобы бросать код на вас, есть два понятия, которые являются ключом к пониманию того, как JS обрабатывает обратные вызовы и асинхронность. (Это вообще слово?)

Модель цикла событий и параллелизма

Вам нужно знать три вещи; Очередь; цикл события и стек

В широких, упрощенных терминах цикл событий похож на менеджера проекта, он постоянно слушает любые функции, которые хотят запускать и обмениваться между очередью и стеком.

 while (queue.waitForMessage()) { queue.processNextMessage(); } 

Когда он получает сообщение для запуска чего-то, он добавляет его в очередь. Очередь - это список вещей, которые ждут выполнения (например, ваш запрос AJAX). представьте себе это так:

  1. call foo.com/api/bar using foobarFunc 2. Go perform an infinite loop ... and so on 

Когда одно из этих сообщений собирается выполнить, оно выталкивает сообщение из очереди и создает стек, стек - все, что нужно выполнить JS для выполнения инструкции в сообщении. Итак, в нашем примере ему сказали позвонить foobarFunc

 function foobarFunc (var) { console.log(anotherFunction(var)); } 

Так что все, что foobarFunc должно выполнить (в нашем случае anotherFunction ), будет вдавлено в стек. выполненный, а затем забытый - цикл события затем переместится на следующую вещь в очереди (или прослушивает сообщения)

Главное здесь - порядок исполнения. То есть

КОГДА что-то будет запущено

Когда вы выполняете вызов с использованием AJAX на внешнюю сторону или запускаете любой асинхронный код (например, setTimeout), Javascript зависит от ответа, прежде чем он сможет продолжить.

Большой вопрос, когда он получит ответ? Ответ в том, что мы не знаем, поэтому цикл событий ждет, когда это сообщение скажет: "Эй, забери меня". Если JS просто ждал этого сообщения синхронно, ваше приложение замерзнет, и оно сосать. Поэтому JS продолжает выполнение следующего элемента в очереди, ожидая, пока сообщение не будет добавлено обратно в очередь.

Именно поэтому с асинхронной функциональностью мы используем вещи, называемые обратными вызовами . Это похоже на обещание буквально. Как и я, я обещаю что-то вернуть в какой-то момент. JQuery использует определенные обратные вызовы, называемые deffered.done deffered.fail и deffered.always (среди прочих). Вы можете увидеть их все здесь

Итак, вам нужно передать функцию, которая в какой-то момент должна выполнить с переданными ей данными.

Поскольку обратный вызов не выполняется немедленно, но в более позднее время важно передать ссылку на функцию, которую он не выполнил. так

 function foo(bla) { console.log(bla) } 

поэтому большую часть времени (но не всегда) вы пройдете foo not foo()

Надеюсь, это будет иметь смысл. Когда вы сталкиваетесь с такими вещами, которые кажутся запутанными, я настоятельно рекомендую полностью прочитать документацию, чтобы хотя бы понять ее. Это сделает вас намного лучше разработчиков.

9
ответ дан Matthew Brent 04 мая '18 в 18:56 2018-05-04 18:56
  • 1
  • 2

Другие вопросы по меткам или Задайте вопрос