Am o funcție " foo " care face o cerere Ajax. Cum pot returna un răspuns de "foo"?
Am incercat revenirea la valoarea de "succes" apel invers, precum și atribuirea răspuns la o variabilă locală în funcție și revenirea că unul, dar nici unul din aceste moduri de fapt, acest răspuns.
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`.
→ Pentru o explicație generală de comportament asincron cu diferite exemple, vă rugăm să consultați https://stackoverflow.com/q/23667086/218196
→ Dacă ai deja să înțeleagă problema, treceți la soluțiile posibile de mai jos.
Problema
A O în [Ajax][1] reprezintă asincron . Asta înseamnă că trimiterea cererii (sau, mai degrabă primirea răspunsului) este scos din fluxul de executie normal. În exemplul tău,
$.ajax
se întoarce imediat și următoarea declarație,return rezultat;
, este executat înainte de funcția trecut ca "succes" callback fost numit chiar. Aici este o analogie care, sperăm, face diferența între sincrone și asincrone flux mai clar:Sincrone
Imaginați-vă că face un apel telefonic de la un prieten și cere-i să uite ceva pentru tine. Deși ar putea dura un timp, tu stai pe telefon și să mă uit în spațiu, până când prietenul tău îți dă răspunsul de care aveți nevoie. Același lucru se întâmplă atunci când face un apel de funcție care conține "normale" codul:
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
Chiar dacă findItem
ar putea lua o lungă perioadă de timp pentru a executa orice cod care vine după var element = findItem();
are să aștepte ** până la funcția returnează rezultatul.
Sună-ți prietenul, din nou, pentru același motiv. Dar de data asta spune-i că ești într-o grabă și că ar trebui să te sun înapoi de pe telefonul mobil. Închide tu, ieși din casă și să faci ce ți-ai planificat să faci. Odată ce prietenul tău te sună înapoi, ai de-a face cu informațiile primite de la el pentru tine. Ca's exact ceea ce's întâmplă atunci când faci o cerere Ajax.
findItem(function(item) {
// Do something with item
});
doSomethingElse();
Îmbrățișarea naturii asincrone a JavaScript! În timp ce anumite operații asincrone oferi sincron omologii (deci, nu "Ajax"), l's, în general, descurajat să le folosească, mai ales într-un context de browser. De ce este rău întrebi? JavaScript rulează în firul UI de browser-ul și orice proces care rulează va bloca UI, făcându-l nu raspund. În plus, există o limită superioară a timpului de execuție pentru JavaScript și browser-ul va cere utilizatorului dacă să continue executarea sau nu. Toate acest lucru este foarte rău experiența utilizatorului. Utilizatorul câștigat't fi în măsură să spun dacă totul este de lucru bine, sau nu. În plus, efectul va fi mai rău pentru utilizatorii cu o conexiune lentă. În următoarele ne vom uita la trei soluții diferite, care sunt toate de clădire de pe partea de sus a reciproc:
atunci()
(ES2015+, disponibile în browserele mai vechi, dacă utilizați una dintre multe promit biblioteci)
Toate trei sunt disponibile în browserele actuale, și nod 7+. asincron/așteaptă
ECMAScript versiune a lansat în 2017 introdus sintaxa-nivel de suport pentru asynchronous funcții. Cu ajutorul "asincron" și așteaptă
, poti scrie asincron într-o "sincron stil". Codul este încă asincron, dar's ușor de citit/înțeles.
asincron/așteaptă
se bazează pe partea de sus de promisiuni: o "asincron" funcția returnează întotdeauna o promisiune. așteaptă
"desface" o promisiune și duce fie la valoarea promisiunea a fost rezolvat, cu sau aruncă o eroare dacă promisiunea a fost respins.
Important: puteți utiliza numai așteaptă
în interiorul unei "asincron" funcția. Acum, la nivel de top așteaptă
e't încă susținută, așa că ar trebui să facă un asincron VIATA (Invocat Imediat Funcția de Exprimare) pentru a începe o "asincron" contextul.
Puteți citi mai multe despre "asincron" și așteaptă
pe MDN.
Aici este un exemplu că se bazează pe partea de sus de întârziere de mai sus:
// 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);
})();
asincron/așteaptă
. Puteți sprijini, de asemenea, mai mari medii de transformarea cod ES5 cu ajutorul regenerator (sau instrumente care folosesc regenerator, precum Babel). Un apel invers este pur și simplu o funcție a trecut la o altă funcție. Că altă funcție se poate apela funcția trecut ori de câte ori este gata. În contextul unui proces asincron, apelul va fi numit ori de câte ori procesul asincron se face. De obicei, rezultatul este trecut la apel invers. În exemplu de întrebare, puteți face " foo "accepta un apel și de a folosi ca "succes" callback. Deci, acest lucru
var result = foo();
// Code that depends on 'result'
devine
foo(function(result) {
// Code that depends on 'result'
});
Aici am definit funcția "inline", dar puteți trece orice funcție referință:
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
"foo" în sine este definită după cum urmează:
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
callback
se va referi la funcția vom trece la " foo "atunci când îi spunem și noi pur și simplu trece pe la "succes". I. e. odată ce cererea Ajax este de succes, $.ajax
sun callback
și trece răspuns la apel invers (care pot fi menționate cu "rezultat", deoarece acest lucru este cum am definit callback).
Puteți, de asemenea, procesul de răspuns înainte de a trece la callback:
function foo(callback) {
$.ajax({
// ...
success: function(response) {
// For example, filter the response
callback(filtered_response);
}
});
}
De Promit API este o caracteristică nouă de ECMAScript 6 (ES2015), dar are bun sprijin browser-ul deja. Există, de asemenea, multe biblioteci care pun în aplicare standardul Promite API și de a oferi metode suplimentare pentru a ușura utilizarea și compoziția asincron funcții (de exemplu, bluebird). Promisiunile sunt containere pentru viitor valori. Când promisiunea primește o valoare (este rezolvat) sau atunci când este anulat (respins), se notifică toate "ascultătorii" care doresc să acceseze această valoare. Avantajul peste câmpie callback este că acestea vă permit să decupla cod, și sunt mai ușor de a compune. Aici este un exemplu simplu folosind o promisiune:
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).
});
Aplicat nostru apel Ajax am putea folosi promisiuni de genul asta:
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
});
Descrie toate avantajele pe care promit oferta este dincolo de domeniul de aplicare de acest răspuns, dar dacă ai scrie cod nou, ar trebui să ia serios în considerare-le. Ele oferă o mare de abstractizare și de separare de cod. Mai multe informații despre promisiuni: HTML5 pietre - JavaScript Promite
Amânat obiecte sunt jQuery's personalizate punerea în aplicare a promisiunilor (înainte Promisiunea API a fost standardizat). Se comporta aproape ca promisiuni, dar expune-o ușor diferite API. Fiecare metoda Ajax jQuery deja returnează o "amânat obiect" (de fapt, o promisiune de amânare la obiect) care puteți reveni la funcția de:
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
Păstrați în minte că de promisiuni și amânări obiecte sunt doar containere pentru o valoare viitoare, ele nu sunt în valoare în sine. De exemplu, să presupunem că ați avut următoarele:
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
}
Acest cod înțelege cele de mai sus asincrone probleme. În mod special, $.ajax()
nu't înghețe cod în timp ce se verifică '/parola' pagina de pe server - trimite o cerere la server și în timp ce așteaptă, imediat returnează un jQuery Ajax Amânat obiect, nu un răspuns de la server. Asta înseamnă că dacă declarația este de gând pentru a obține întotdeauna acest Amânat obiect, tratează-l ca "adevărat", și se procedează ca și cum utilizatorul este logat. Nu-i bine.
Dar fix este simplu:
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
});
După cum am menționat, unele(!) operații asincrone au sincrone omologii. Eu nu't avocatul utilizarea lor, dar pentru completitudine' dumnezeu, aici este modul în care s-ar efectua un sincron de telefon:
Dacă ai folosi direct o XMLHTTPRequest
obiect, trece "false" ca al treilea argument pentru a .deschide
.
Dacă utilizați jQuery, puteți seta "asincron" opțiunea de a "false". Rețineți că această opțiune este depreciat deoarece jQuery 1.8.
Apoi, puteți fie utiliza încă un "succes" callback sau acces la responseText
proprietate a jqXHR obiect:
function foo() {
var jqXHR = $.ajax({
//...
async: false
});
return jqXHR.responseText;
}
Dacă utilizați orice alt jQuery Ajax metodă, cum ar fi $.get
, $.getJSON
, etc., trebuie să-l schimbe la $.ajax
(deoarece puteți trece doar parametrii de configurare a $.ajax
).
Atenție! Nu este posibil să se facă un sincron JSONP cerere. JSONP prin însăși natura sa este întotdeauna asincron (un motiv mai mult să nu ia în considerare această opțiune).
[1]: https://en.wikipedia.org/wiki/Ajax_(de programare)
Codul ar trebui să fie ceva de genul asta:
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 făcut o treabă bună de scris un raspuns pentru oamenii care folosesc jQuery pentru AJAX, am'am decis să oferim o alternativă pentru cei care sunt't.
Acesta este un scurt rezumat al "Explicație problema" de la alt raspuns, daca're nu sunt sigur după ce a citit acest lucru, citiți asta.
A O în AJAX standuri pentru asincron. Asta înseamnă că trimiterea cererii (sau, mai degrabă primirea răspunsului) este scos din fluxul de executie normal. În exemplul tău, .trimite
a se întoarce imediat și următoarea declarație, return rezultat;
, este executat înainte de funcția trecut ca "succes" callback fost numit chiar.
Aceasta înseamnă că atunci când te're revenind, ascultător ai'am definit nu executa încă, ceea ce înseamnă valoarea pe care o're revenirea nu a fost definit.
Aici este o analogie simplă
function getFive(){
var a;
setTimeout(function(){
a=5;
},10);
return a;
}
[(Vioara)][2]
Valoarea " a "întors este" nedefinit " din a=5
parte nu a executat încă. AJAX acte de genul asta, ai're returnarea contravalorii înainte ca serverul să avem șansa de a spune browser-ului ce este valoarea.
O posibilă soluție la această problemă este de a cod re-actively , spunându-programul tău ce să facă atunci când calculul finalizat.
function onComplete(a){ // When the code completes, do this
alert(a);
}
function getFive(whenDone){
var a;
setTimeout(function(){
a=5;
whenDone(a);
},10);
}
Acest lucru este numit CPS. Practic, ne-am're trece getFive
o acțiune pentru a efectua, atunci când se completează, am're spune codul nostru cum să reacționeze atunci când un eveniment completează (ca AJAX nostru de apel, sau, în acest caz timeout).
Utilizare ar fi:
getFive(onComplete);
Care ar trebui să alerteze "5" la ecranul. [(Vioara)][4].
De fapt, există două moduri de modul de a rezolva acest lucru:
Cum pentru sincrone AJAX, don't face! Felix's a răspunde ridică unele argumente convingătoare despre ce-l's o idee rea. Pentru a rezuma, l'll înghețe de utilizator's browser până când serverul returnează răspunsul și de a crea o foarte proastă experiență de utilizator. Aici este un alt scurt rezumat luat de la MDN pe ce:
XMLHttpRequest suportă atât sincron și asincron de comunicare. În general, cu toate acestea, cereri asincrone ar fi de preferat să sincron cereri pentru motive de performanță.
pe scurt, sincron cereri bloc executarea de cod... ...acest lucru poate cauza probleme grave...
Dacă have pentru a face aceasta, puteți trece un steag: Iată cum:
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);
}
Lasa-ti funcția de a accepta un apel. În exemplul de cod " foo " pot fi făcute pentru a accepta un apel. Am'll spune codul nostru cum să react când " foo " completeaza.
Deci:
var result = foo();
// code that depends on `result` goes here
Devine:
foo(function(result) {
// code that depends on `result`
});
Aici am trecut pe lângă o funcție anonim, dar am putea la fel de ușor trece o trimitere la o functie existenta, făcându-l arate ca:
function myHandler(result) {
// code that depends on `result`
}
foo(myHandler);
Pentru mai multe detalii despre acest tip de apel invers design-ul este terminat, verificați Felix's a răspunde.
Acum, las's a defini foo sine pentru a acționa în consecință
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();
}
[(vioara)][6]
Acum ne-am făcut noastre foo funcția accepta o acțiune pentru a rula atunci când AJAX completeaza cu succes, putem extinde această suplimentare de verificare în cazul în care răspunsul nu este starea 200 și acționează în consecință (a se putea crea handler și cum). Mod eficient de a rezolva problema noastră.
Daca're încă greu să înțeleagă acest citeste AJAX ghidul noțiuni de bază la MDN.
XMLHttpRequest 2 (mai întâi de toate, citiți răspunsuri la Benjamin Gruenbaum & Felix Kling) Dacă tu nu't folosi jQuery și o scurtă XMLHttpRequest 2 care funcționează pe browsere moderne și, de asemenea, pe browsere mobile îți sugerez să utilizați-l în acest fel:
function ajax(a, b, c){ // URL, callback, just a placeholder
c = new XMLHttpRequest;
c.open('GET', a);
c.onload = b;
c.send()
}
După cum puteți vedea:
this.response
Sau dacă pentru un motiv oarecare bind()
callback pentru o clasă:
e.target.response
Exemplu:
function callback(e){
console.log(this.response);
}
ajax('URL', callback);
Sau (cea de mai sus este mai bine funcții anonime sunt mereu o problema):
ajax('URL', function(e){console.log(this.response)});
Acum daca vrei ceva mai complex, folosind post și FormData puteți extinde cu ușurință această funcție:
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)
}
Din nou ... l's un foarte scurt funcția, dar nu obține & post. Exemple de utilizare:
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
Sau trece un plin element de formular (document.getElementsByTagName('forma')[0]
):
var fd = new FormData(form);
x(url, callback, 'post', fd);
Sau pentru a seta anumite valori personalizate:
var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);
Așa cum am menționat în comentariul utilizarea de eroare && sincron se rupe complet punctul de a răspunde. Care este un scurt frumos mod de a folosi Ajax în mod adecvat? Error handler
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);
displayAjax () " sub " acest lucru.statusText " ca " Metoda nu este Permis
.
În al doilea caz, este pur și simplu funcționează. Trebuie să verificați la partea de server dacă ai trecut dreptul de a posta date.
cross-domain nu a permis aruncă în eroare în mod automat.
În răspuns de eroare, nu există coduri de eroare.
Nu este numai asta.tipcare este setat la eroare. De ce adauga un handler de erori dacă avem nici un control asupra erorilor? Cele mai multe dintre erori sunt returnate în interiorul acest lucru în funcția de apel invers
displayAjax()`.
Deci: Nu este nevoie de eroare verifică dacă ai're capabil de a copia și lipiți URL-ul în mod corespunzător. ;)
PS: Ca primul test am scris x('x', displayAjax)..., și a primit un răspuns...??? Așa că am verificat dosarul în care HTML este situat, și acolo a fost un fișier numit 'x.xml'. Deci, chiar dacă ați uitat extensia de fișier XMLHttpRequest 2 VA GĂSI. Eu LOL'd Citește un fișier sincron Don't face asta. Dacă doriți pentru a bloca browser-ul pentru o în timp ce încărca un mare `.txt file sincron.
function omg(a, c){ // URL
c = new XMLHttpRequest;
c.open('GET', a, true);
c.send();
return c; // Or c.response
}
Acum puteți face
var res = omg('thisIsGonnaBlockThePage.txt');
Funcțiile de mai sus sunt pentru utilizarea de bază. Dacă doriți să se EXTINDĂ funcția... Da, puteți. Am'm, folosind o mulțime de Api-uri și una dintre primele funcții I se integreze în fiecare pagină HTML este primul Ajax funcție de acest răspuns, cu doar... Dar puteți face o mulțime de lucruri cu XMLHttpRequest 2: Am facut un download manager (folosind variază pe ambele părți cu cv-ul, filereader, sistemul de fișiere), diverse imagini resizers convertoare folosind panza, popula web baze de date SQL cu base64images și mult mai mult... Dar, în aceste cazuri, ar trebui să creați o funcție doar pentru acest scop... uneori ai nevoie de o pată de cerneală, matrice tampoane, puteți seta cap, suprascrie tip mime și există o mult mai mult... Dar întrebarea este: cum pentru a reveni un Ajax răspuns... (am adăugat un mod ușor.)
Acest lucru înseamnă AngularJS, jQuery (cu amânată), nativ XHR's înlocuire (fetch), EmberJS, BackboneJS's a salva sau orice nod de bibliotecă, care se întoarce de promisiuni. Codul ar trebui să fie ceva de genul asta:
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.
JavaScript concurenta model în browser-ul și de pe server cu NodeJS/io.js este asynchronous și reactive.
Ori de câte ori ai sunat-o metodă care returnează o promisiune, "apoi" stivuitoare sunt always execută asincron - care este, după codul de mai jos-le că nu este într-o .apoi
handler.
Aceasta înseamnă că atunci când te're revenind "date " a", apoi " handler'am definit nu executa încă. Aceasta înseamnă că valoarea pe care o're revenirea nu a fost stabilit la valoarea corectă în timp.
Aici este o analogie simplă pentru problema:
function getFive(){
var data;
setTimeout(function(){ // set a timer for one second in the future
data = 5; // after a second, do this
}, 1000);
return data;
}
document.body.innerHTML = getFive(); // `undefined` here and not 5
Valoarea de "date" este "nedefinit" din anii date = 5
parte nu a executat încă. Probabil ea se va executa într-o secundă, dar până în acel moment este irelevant pentru valoarea returnată.
Deoarece operațiunea nu s-a întâmplat încă (AJAX, server de apel, IO, timer) ai're returnarea contravalorii înainte de solicitarea avut ocazia să-i spui codul ce este valoarea.
O posibilă soluție la această problemă este de a cod re-actively , spunându-programul tău ce să facă atunci când calculul finalizat. Promite în mod activ permite acest lucru, fiind temporale (time-sensitive) în natură.
O Promisiune este o value peste time. Promisiuni au stat, au început în așteptare cu nici o valoare și pot soluționa la:
Las's vedem cum putem rezolva problema de mai sus cu promisiuni. În primul rând, să's a demonstra înțelegerea noastră de promisiune membre de mai sus, cu ajutorul Promit constructor pentru a crea o funcție de întârziere:
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);
});
}
Acum, după ce ne-am convertit setTimeout pentru a folosi promisiuni, putem folosi", apoi " pentru a face conta:
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);
});
}
function getFive(){
// we're RETURNING the promise, remember, a promise is a wrapper over our value
return delay(100).then(function(){ // when the promise is ready
return 5; // return the value 5, promises are all about return values
})
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){
document.body.innerHTML = five;
});
Practic, în loc de a se întoarce o value care putem't face concurenta modelului - am're revenind un wrapper pentru o valoare care ne poate unwrap cu "apoi". L's ca o cutie puteți deschide cu "apoi".
Acest lucru iese la fel de originale apel API, puteți:
function foo() {
// RETURN the promise
return fetch("/echo/json").then(function(response){
return response.json(); // process it inside the `then`
});
}
foo().then(function(response){
// access the value inside the `then`
})
Deci, aceasta funcționează la fel de bine. Am'am învățat să ne putem't returna valori de la deja apeluri asincrone dar ne putem folosi de promisiuni și le lanț pentru a efectua prelucrarea. Acum știm cum pentru a reveni răspuns la un apel asincron.
ES6 introduce generatoarele care sunt funcțiile care pot reveni în mijloc și apoi se reia punctul ei au fost la. Acest lucru este de obicei util pentru secvențe, de exemplu:
function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
yield 1;
yield 2;
while(true) yield 3;
}
Este o funcție care returnează un iterator peste secvența 1,2,3,3,3,3,....
care poate fi reiterat. În timp ce acest lucru este interesant pe cont propriu și se deschide camera pentru o mulțime de posibilitate există un anumit caz interesant.
Dacă secvența pe care am're producătoare este o secvență de acțiuni, mai degrabă decât numere - putem întrerupe funcția ori de câte ori o acțiune este generată și așteptați pentru ca aceasta înainte de a ne relua funcția. Deci, în loc de o secvență de numere, avem nevoie de o secvență de future valori - care este: promisiuni.
Acest oarecum complicat, dar foarte puternic truc ne permite să scrie cod asincron într-un mod sincron. Există mai multe "alergători" care face acest lucru pentru tine, scris unul este un scurt câteva linii de cod, dar este dincolo de domeniul de aplicare al acestui răspuns. Am'll fi folosind Bluebird's Promit.coroutine aici, dar există și alte ambalaje ca
co " sau " Q. asincron`.
var foo = coroutine(function*(){
var data = yield fetch("/echo/json"); // notice the yield
// code here only executes _after_ the request is done
return data.json(); // data is defined
});
Această metodă returnează o promisiune, pe care o putem consuma din alte coroutines. De exemplu:
var main = coroutine(function*(){
var bar = yield foo(); // wait our earlier coroutine, it returns a promise
// server call done here, code below executes when done
var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
console.log(baz); // runs after both requests done
});
main();
În ES7, aceasta este în continuare standardizate, există mai multe propuneri, chiar acum, dar în toate dintre ele pot așteaptă
promit. Aceasta este doar "zahăr" (mai frumos sintaxă) pentru ES6 propunerea de mai sus prin adăugarea "asincron" și așteaptă
cuvinte cheie. A face exemplul de mai sus:
async function foo(){
var data = await fetch("/echo/json"); // notice the await
// code here only executes _after_ the request is done
return data.json(); // data is defined
}
Ea se întoarce încă o promisiune la fel :)
Utilizați Ajax incorect. Ideea nu este să se întoarcă nimic, dar în loc de mână pe datele de la ceva numit o funcție de apel invers, care se ocupă de date.
Asta este:
function handleData( responseData ) {
// Do what you want with the data
console.log(responseData);
}
$.ajax({
url: "hi.php",
...
success: function ( data, status, XHR ) {
handleData(data);
}
});
Revenind nimic în prezenta handler nu va face nimic. Trebuie să, în loc să fie parte pe date, sau de a face ce vrei cu ea direct în interiorul succes funcție.
Cea mai simplă soluție este de a crea o funcție JavaScript și apel pentru Ajax "succes" callback.
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);
});
Pentru cei care sunt utilizați AngularJS, pot descurca în această situație, folosind Promisiuni
.
Aici se spune,
Promisiunile pot fi folosite pentru a unnest asincron funcții și permite o pentru lant mai multe funcții împreună.
Puteți găsi o frumoasă explicație aici de asemenea.
Exemplu găsite în documente menționate mai jos.
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.
În Angular2 cu privire la următoarele exemplu, dar [recomandat][5] pentru a utiliza
Observabile " cu " Angular2`.
search(term: string) {
return this.http
.get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
.map((response) => response.json())
.toPromise();
}
Puteți consuma în acest fel,
search() {
this.searchService.search(this.searchField.value)
.then((result) => {
this.result = result.artists.items;
})
.catch((error) => console.error(error));
}
Vezi originala post aici. Dar de Scris nu are suport nativ es6 Promite, dacă doriți să-l folosească, s-ar putea nevoie de plugin pentru asta.
În plus, aici este promisiuni spec definesc aici.
Cele mai multe răspunsuri aici da sugestii utile pentru atunci când aveți un singur asincron operațiune, dar, uneori, acest lucru apare atunci când aveți nevoie pentru a face o operație asincronă pentru fiecare intrarea într-o matrice sau o altă listă-ca structura. Tentația este de a face acest lucru:
// WRONG
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log(results); // E.g., using them, returning them, etc.
Exemplu:
// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log("Results:", results); // E.g., 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;
}
Motivul pentru care nu't de lucru este că apelurile de la doSomethingAsync` am't rula încă de când te're încercarea de a utiliza rezultatele. Deci, dacă aveți o matrice (sau lista de vreun fel) și vreau să fac asincron operațiuni pentru fiecare intrare, aveți două opțiuni: să Facă operațiuni în paralel (care se suprapun), sau în serie (una după alta în ordine).
Puteți începe tot de pe ei și de a urmări cât de multe callback te're așteaptă, și de a folosi apoi rezultatele'am ajuns atât de multe telefoane:
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); // E.g., using the results
}
});
});
Exemplu:
var theArray = [1, 2, 3];
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); // E.g., using the results
}
});
});
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;
}
(Am putea face departe cu așteaptă
și de a folosi doar rezultatele.lungimea === theArray.lungimea, dar care ne lasă deschisă posibilitatea ca theArray
este schimbat în timp ce apeluri sunt remarcabile...)
Observați cum vom folosi la "index" de la "forEach" pentru a salva rezultatul în "rezultate" în aceeași poziție ca la intrare se referă la, chiar dacă rezultatele sosiți în afara de ordine (din asincron apeluri don't neapărat complete, în ordinea în care au fost pornite).
Dar ce se întâmplă dacă aveți nevoie pentru a revenirea cele rezultate dintr-o funcție? Ca alte răspunsuri au subliniat, puteți't; că trebuie să aibă funcția de a accepta și de apel un apel (sau de a reveni o Promisiune). Aici's un apel invers versiune:
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);
});
Exemplu:
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([1, 2, 3], function(results) {
console.log("Results:", results);
});
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;
}
Sau aici's o versiune revenind o Promisiune
în loc:
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);
});
Desigur, dacă doSomethingAsync
trecut ne erorile, am'd folosi "respinge" de a respinge promisiunea, când am primit o eroare.)
Exemplu:
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([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
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;
}
(Sau alternativ, ai putea face un wrapper pentru doSomethingAsync` care returnează o promisiune, și apoi face mai jos...)
Dacă doSomethingAsyncvă oferă o [Promisiune][1], puteți utiliza [
Promit.toate`]2:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(function(entry) {
return doSomethingAsync(entry);
}));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Dacă știi că doSomethingAsync
va ignora un al doilea și al treilea argument, puteți trece direct la "harta" ("harta" apelurile sale de apel invers cu trei argumente, dar cei mai mulți oameni folosesc doar primele cele mai multe ori):
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Exemplu:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
Rețineți că Promit.toate` rezolvă promisiunea cu o serie de rezultate dintre toate promisiunile pe care ți-l dau atunci când acestea sunt toate rezolvate, sau nu respinge promisiunea de când în primul rând a promite să vă dau o respinge.
Să presupunem că don't vreau operațiuni să fie în paralel? Dacă doriți să rulați-le unul după altul, trebuie să așteptați pentru fiecare operațiune pentru a finaliza înainte de a începe următoarea. Aici's un exemplu de funcție care se ocupă de asta și numește un apel invers cu rezultatul:
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);
});
(Din ne're face treaba în serie, putem folosi doar rezultatele.push(rezultat)deoarece știm că a câștigat't obține rezultate de ordine. În cele de mai sus ne-ar fi folosit
rezultatele[index] = rezultat;`, dar în unele dintre următoarele exemple nu ne't au un indice de utilizare.)
Exemplu:
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([1, 2, 3], function(results) {
console.log("Results:", results);
});
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;
}
(Sau, din nou, să construiască un înveliș pentru doSomethingAsync
, care vă oferă o promisiune și de a face cele de mai jos...)
Dacă doSomethingAsyncvă oferă o Promisiune, dacă puteți utiliza ES2017+ sintaxă (poate cu un transpiler ca [Babel](http://babeljs.io)), puteți utiliza un ["asincron" funcția][3] [
de-a][4] și [
așteaptă`]5:
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);
});
Exemplu:
async function doSomethingWith(theArray) {
const results = [];
for (const entry of theArray) {
results.push(await doSomethingAsync(entry));
}
return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
Dacă poți't folosi ES2017+ sintaxă (încă), puteți utiliza o variație pe "Promisiunea de a reduce" model (acest lucru este mult mai complexă decât de obicei Promisiunea de a reduce pentru că ne-am're nu trece rezultatul din una în alta, dar în loc colectarea de rezultatele lor într-o matrice):
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);
});
Exemplu:
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([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
...care este mai puțin greoaie cu ES2015+ săgeată funcții:
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);
});
Exemplu:
function doSomethingWith(theArray) {
return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
results.push(result);
return results;
})), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
console.log("Results:", results);
});
function doSomethingAsync(value) {
console.log("Starting async operation for " + value);
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Completing async operation for " + value);
resolve(value * 2);
}, Math.floor(Math.random() * 200));
});
}
.as-console-wrapper {
max-height: 100% !important;
}
Au o privire la acest exemplu:
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);
});
});
După cum puteți vedea getJokeeste **revenind un** rezolvat **promit** (este rezolvată atunci când se întorc
res.date.valoarea`). Așteaptă până când $http.ia cererea este finalizată și apoi consola.log(res.glumă) este executat (ca normal asincron flow).
Aceasta este plnkr:
http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/
ES6 mod (asincron - așteaptă)
(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);
});
})();
Acesta este unul dintre locurile care două moduri de legare date sau concept de magazin care's a folosit în multe noi cadre JavaScript va lucra mare pentru tine...
Deci, dacă utilizați Unghiulare, de a Reacționa sau orice alte cadre care fac două moduri de legare date sau concept de magazin această problemă este pur și simplu fix pentru tine, atât în cuvânt ușor, rezultatul este "nedefinit" la prima etapă, așa că au rezultat = undefined` înainte de a primi datele, apoi, de îndată ce veți obține rezultatul, acesta va fi actualizat și a fost transferat la noua valoare care a răspuns de apel Ajax...
Dar cum poți să o faci din pură javascript sau jQuery de exemplu, ca te-a cerut în această întrebare?
Puteți utiliza un callback, promit și, recent observabile să-l ocupe pentru tine, de exemplu în promisiuni avem o funcție ca succesul()sau
atunci()` care va fi executat atunci când datele dvs. este gata pentru tine, la fel cu apel invers sau subscribe funcție observabile.
De exemplu, în cazul pe care îl utilizați jQuery, puteți face ceva de genul asta:
$(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
});
Pentru mai multe informații studiu despre promisiuni și observabile care sunt mai noi moduri de a face acest lucru asincron umpluturi.
O altă abordare pentru a returna o valoare dintr-o funcție asincron, este de a trece într-un obiect care se va stoca rezultatul din funcție asincron.
Aici este un exemplu de același:
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');
});
Eu sunt, folosind "rezultat" obiect pentru a stoca valoarea de timpul de funcționare asincron. Acest lucru permite ca rezultatul să fie disponibile chiar și după asincron loc de muncă.
Eu folosesc această abordare o multime. Aș fi interesat să știu cât de bine această abordare funcționează în cazul în care cablajul rezultatul înapoi prin consecutive module este implicat.
În timp ce promisiunile și callback funcționează bine în multe situații, este o durere în partea din spate a exprima ceva de genul:
if (!name) {
name = async1();
}
async2(name);
Te'd sfârși prin a trece prin async1
; verificați dacă "nume" este nedefinit sau nu și de apel invers în mod corespunzător.
async1(name, callback) {
if (name)
callback(name)
else {
doSomething(callback)
}
}
async1(name, async2)
În timp ce acesta este okay în mici exemple devine enervant atunci când aveți o mulțime de cazuri similare și eroare de manipulare implicate.
`Fibrele ajuta în rezolvarea problemei.
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
}
Puteți checkout proiectului aici.
Răspunsul scurt este, trebuie să pună în aplicare un apel invers astfel:
function callback(response) {
// Here you can do what ever you want with the response object.
console.log(response);
}
$.ajax({
url: "...",
success: callback
});
Următorul exemplu am scris arată cum să
Acest exemplu de lucru este auto-conținute. Se va defini un simplu obiect cerere care utilizează fereastra XMLHttpRequest
obiect pentru a efectua apeluri. Se va defini o funcție de simplu să așteptați pentru o grămadă de promisiuni să fie finalizată.
Context. Exemplul este interogarea Spotify Web API endpoint în scopul de a căuta pentru "listă de redare" obiecte pentru un anumit set de șiruri de interogare:
[
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
Pentru fiecare element, o nouă Promisiune va foc un bloc - ExecutionBlock
, analiza urmare, programa un nou set de promisiuni bazate pe rezultatul matrice, care este o listă de Spotify "utilizator" obiecte și de a executa noile HTTP apel în ExecutionProfileBlock
asincron.
Apoi, puteți vedea o imbricate Promit structura, care vă permite să ruleze mai multe și complet asincron imbricate HTTP apeluri, și să se alăture rezultate de la fiecare subgrup de apeluri prin Promisiune.toate`.
NOTĂ Recent Spotify "căutare" APIs va necesita un token de acces să fie specificate în cerere antete:
-H "Authorization: Bearer {your access token}"
Deci, aveți pentru a rula exemplul următor aveți nevoie pentru a pune token-ul de acces în anteturile de cerere:
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" />
Am discutat pe larg această soluție aici.
Acest lucru este destul de simplu:
Aici's o versiune de lucru-ți de cod:
(async function(){
var response = await superagent.get('...')
console.log(response)
})()
vă așteaptă este acceptată în toate browserele actuale și nodul 8
L's o problemă foarte frecvente care ne confruntăm în timp ce se luptă cu 'mistere' de JavaScript. Lasă-mă să încerc descifrării acestui mister astăzi.
Las's începe cu o simplă funcție JavaScript:
function foo(){
// do something
return 'wohoo';
}
let bar = foo(); // bar is 'wohoo' here
Ca's un simplu sincron funcția de apel (în cazul în care fiecare linie de cod este 'terminat cu treaba' înainte de următoarea secvență), iar rezultatul este același lucru cum era de așteptat.
Acum las's a adăuga un pic de poftă de mâncare, prin introducerea pic de întârziere în funcția noastră, astfel încât toate liniile de cod nu sunt 'terminat' în secvență. Astfel, se va emula asincron comportament de funcția :
function foo(){
setTimeout( ()=>{
return 'wohoo';
}, 1000 )
}
let bar = foo() // bar is undefined here
Deci nu te duci, că întârzierea rupt doar funcționalitatea ne-am așteptat! Dar ce anume s-a întâmplat ? Ei bine, l's, de fapt destul de logic daca te uiti la cod. funcția foo()
, la executie, returnează nimic (astfel revenit valoare este "nedefinit"), dar ea nu începe un cronometru, care execută o funcție după 1s pentru a reveni 'wohoo'. Dar, după cum puteți vedea, valoarea pe care o's repartizat la bar este întors imediat chestii de la foo(), nu orice altceva care vine mai târziu.
Deci, cum putem rezolva această problemă?
Las's cere funcția noastră pentru un PROMIT. Promisiunea este într-adevăr despre ceea ce înseamnă : înseamnă că funcția de garanții pentru a oferi cu orice ieșire devine în viitor. deci sa's a-l văd în acțiune pentru problema de mai sus :
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'
});
Astfel, rezumatul este - de a aborda asincron funcții, cum ar fi bazate pe ajax apeluri etc., puteți utiliza o promisiune de a rezolva
valoarea (pe care intenționați să reveniți). Astfel, în scurt te rezolva valoare în loc de revenirea, în asincron funcții.
În afară de utilizarea apoi/prind să lucreze cu promisiuni, există o altă abordare. Ideea este de a *recunoaște o funcție asincron* iar apoi *așteptați pentru promisiunile* să rezolve, înainte de a trece la următoarea linie de cod. L's încă doar promisiuni,
sub capota, dar cu un alt sintactice abordare. Pentru a face lucrurile mai clare, puteți găsi o comparație mai jos:
function saveUsers(){
getUsers()
.then(users => {
saveSomewhere(users);
})
.catch(err => {
throw err;
})
}
async function saveUsers(){
try{
let users = await getUsers()
saveSomewhere(users);
}
catch(err){
throw err;
}
}
Puteți utiliza această bibliotecă personalizat (scris folosind Promit) să facă un apel la distanță.
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);
};
});
}
Simplu exemplu de utilizare:
$http({
method: 'get',
url: 'google.com'
}).then(function(response) {
console.log(response);
}, function(error) {
console.log(error)
});
O altă soluție este de a executa cod prin secvențială executor nsynjs.
nsynjs va evalua toate promisiunile secvențial, și a pus să promit rezultat în "date" de proprietate:
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="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Pasul 1. Folie cu funcție de apel invers în nsynjs conștient de înveliș (dacă are promisified versiune, puteți sări peste acest pas):
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;
Pasul 2. Pune sincron logica în funcție de:
function process() {
console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}
Pasul 3. Rulați funcția în mod sincron prin nsynjs:
nsynjs.run(process,this,function () {
console.log("synchronous function finished");
});
Nsynjs va evalua toate operatori și expresii pas-cu-pas, oprindu-se executarea în cazul în care rezultatul unor lent funcție nu este gata.
Mai multe exemple aici: https://github.com/amaksr/nsynjs/tree/master/examples
Js este un singur filet.
Browser-ul poate fi împărțit în trei părți:
1)Bucla Eveniment
2)Web API
3)Eveniment Coada
Eveniment Bucla se execută pentru totdeauna eu.e un fel de buclă infinită.Eveniment Coada este în cazul în care toate dvs. de funcție sunt împinse pe un eveniment(exemplu:click) acest lucru este unul câte unul efectuate de coadă și a pus în buclă Eveniment care executa această funcție și se pregătește pentru următoarea după primul este executat.Acest lucru înseamnă Executarea unei funcții nu't începe până la funcție înainte de a-l în coadă este executat în buclă eveniment.
Acum, să ne cred ca ne-a împins două funcții într-o coadă este pentru obtinerea de date de la server și un alt utilizează aceste date.Am împins serverRequest (funcția) în coadă în primul rând, apoi utiliseData() funcție. serverRequest funcția merge in bucla eveniment și face un apel la server ca nu vom ști niciodată cât de mult timp le va lua pentru a obține date de la server deci acest proces este de așteptat să ia timp și așa ne-am ocupat de evenimentul nostru buclă, astfel, agățat pagina noastră, ca's în cazul în care API Web intra în rolul pe care îl avea această funcție de la bucla de evenimente și oferte cu serverul face bucla eveniment gratuit, astfel încât să putem executa următoarea funcție de coadă.Următoarea funcție în coadă este utiliseData() care merge în buclă dar cauza nu sunt date disponibile merge deșeuri și executarea de următoarea funcție continuă până la sfârșitul cozii de așteptare.(Aceasta se numește Asincron sun eu.e putem face altceva până când vom obține date)
Să presupunem nostru serverRequest() funcția avut o declarație de întoarcere într-un cod, când ne întoarcem de date de la serverul Web API va împinge în coada de așteptare la sfârșitul cozii. Ca împins de la capăt la coadă nu putem utiliza datele sale cât nu există nici o funcție rămasă în coadă de a utiliza aceste date.Astfel, nu este posibil să se întoarcă ceva din Asincron Apel.
Astfel, Soluția la această este de callback sau promit.
O Imagine de la unul din raspunsurile de aici, Corect explică de apel invers de utilizare... Ne dam cu funcția de(funcția utilizând datele returnate de server) pentru funcția de asteptare server.
function doAjax(callbackFunc, method, url) {
var xmlHttpReq = new XMLHttpRequest();
xmlHttpReq.open(method, url);
xmlHttpReq.onreadystatechange = function() {
if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
callbackFunc(xmlHttpReq.responseText);
}
}
xmlHttpReq.send(null);
}
În Codul meu este numit ca
function loadMyJson(categoryValue){
if(categoryValue==="veg")
doAjax(print,"GET","http://localhost:3004/vegetables");
else if(categoryValue==="fruits")
doAjax(print,"GET","http://localhost:3004/fruits");
else
console.log("Data not found");
}
Citeste aici pentru noi metode în ECMA(2016/17) pentru a face apel asincron(@Felix Kling Răspunsul pe partea de Sus) https://stackoverflow.com/a/14220323/7579856