Mám funkci foo
, která dělá žádost Ajax. Jak mohu vrátit odpověď z foo
?
Snažil jsem se vrátit hodnotu z zpětného volání success
a také přiřadit odpověď místní proměnné uvnitř funkce a vrátit ji zpět, ale žádný z těchto způsobů skutečně odpověď nevrátí.
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`.
→ Pro obecnější vysvětlení asynchronního chování s různými příklady viz.Proč je moje proměnná nezměněná poté, co jsem ji změnila uvnitř funkce? - Asynchronní odkaz na kód
→ Pokud problém již porozumíte, přejděte na níže uvedená možná řešení.
Ain Ajax stojí pro asynchronní . To znamená, že odeslání požadavku (nebo spíše přijetí odpovědi) je vyřazeno z běžného prováděcího toku. Ve vašem příkladu se $.ajax
vrací okamžitě a další příkaz, return result;
, se provede před tím, než byla funkce, kterou jste předali jako zpětné volání success
, nazvána.
Zde je analogie, která doufá, že rozdíl mezi synchronním a asynchronním tokem je jasnější:
Představte si, že zavoláte příteli a požádáte ho, aby pro vás něco hledal. I když to může chvíli trvat, počkejte na telefonu a zírajte do vesmíru, dokud vám přítel nepředá odpověď, kterou potřebujete.
Totéž se děje při volání funkce obsahující "normální" kód:
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
Přestože findItem
může trvat dlouho, spuštění jakéhokoliv kódu po var item = findItem();
musí počkat dokud funkce nevrátí výsledek.
Zavoláte znovu svému příteli ze stejného důvodu. Tentokrát mu ale řeknete, že jste ve spěchu a měl by zavolej zpět do vašeho mobilního telefonu. Zavěsíš, opustíš dům a uděláš co chceš. Jakmile vám váš přítel zavolá zpět, jedná se o informace, které vám dal.
To je přesně to, co se děje, když děláte žádost Ajax.
findItem(function(item) {
// Do something with item
});
doSomethingElse();
Namísto čekání na odpověď pokračuje provádění okamžitě a příkaz po provedení volání Ajax. Chcete-li získat odpověď nakonec, poskytnete funkci, která má být volána, jakmile je odpověď přijata, zpětné volání (všimněte si něco? zpětné volání?). Jakýkoliv příkaz přicházející po tomto volání je proveden před voláním zpětného volání.
Přijmout asynchronní povahu JavaScriptu! Zatímco některé asynchronní operace poskytují synchronní protějšky (tak i "Ajax"), je obvykle odradeno je používat, zejména v kontextu prohlížeče.
Proč je to špatné, ptáš se?
JavaScript běží ve vlákně uživatelského rozhraní prohlížeče a jakýkoli dlouhodobě probíhající proces uzamkne uživatelské rozhraní, takže je nereaguje. Kromě toho existuje horní limit doby provádění JavaScriptu a prohlížeč se zeptá uživatele, zda má pokračovat v provádění nebo ne.
To vše je opravdu špatný uživatelský zážitek. Uživatel nebude schopen zjistit, zda vše funguje dobře nebo ne. Účinek bude navíc horší pro uživatele s pomalým připojením.
V následujícím textu se podíváme na tři různá řešení, která jsou postavena na sobě:
async/await
(ES2017 +, dostupné ve starších prohlížečích, pokud používáte transpiler nebo regenerátor)then()
(ES2015 +, dostupné ve starších prohlížečích, pokud používáte jednu z mnoha slibných knihoven)Všechny tři jsou dostupné v aktuálních prohlížečích a uzlu 7+.
async/await
Verze ECMAScript vydaná v roce 2017 představila podpora na úrovni syntaxe pro asynchronní funkce. Pomocí async
a await
můžete psát asynchronně v "synchronním stylu". Kód je stále asynchronní, ale je snazší číst/porozumět.
async/await
navazuje na sliby: funkce async
vždy vrátí slib. await
"rozbalí" slib a buď vyústí v hodnotu, kterou byl slib vyřešen, nebo vyvolá chybu, pokud byl slib odmítnut.
Důležité: Můžete použít pouze await
uvnitř funkce async
. Právě teď await
na nejvyšší úrovni ještě není podporováno, takže možná budete muset provést asynchronní IIFE ( Immedantly Invoked Function Expression ), abyste spustili async
kontext.
Další informace o async
a await
na MDN.
Zde je příklad, který navazuje na zpoždění výše:
// 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);
})();
Aktuální prohlížeč a node version support async/await
. Můžete také podporovat starší prostředí transformací kódu na ES5 pomocí regenerátoru (nebo nástrojů, které používají regenerátor, například Babel ).
Zpětné volání je jednoduše funkce předaná jiné funkci. Tato jiná funkce může funkci předat, kdykoliv je připravena. V kontextu asynchronního procesu bude zpětné volání voláno při každém asynchronním procesu. Obvykle je výsledek předán zpětnému volání.
V příkladu otázky můžete foo
přijmout zpětné volání a použít jej jako success
zpětné volání. Takže tohle
var result = foo();
// Code that depends on 'result'
se stává
foo(function(result) {
// Code that depends on 'result'
});
Zde jsme definovali funkci "inline", ale můžete předat libovolný odkaz na funkci:
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
foo
samotné je definováno následovně:
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
callback
bude odkazovat na funkci, kterou předáváme foo
, když ji voláme a my ji jednoduše předáme success
. Tj. jakmile je požadavek Ajax úspěšný, $.ajax
zavolá callback
a předá odpověď zpětnému volání (které lze označit result
, protože takto jsme definovali zpětné volání).
Odpověď můžete také zpracovat před odesláním zpětnému volání:
function foo(callback) {
$.ajax({
// ...
success: function(response) {
// For example, filter the response
callback(filtered_response);
}
});
}
Je jednodušší psát kód pomocí zpětných volání, než se může zdát. Konec konců, JavaScript v prohlížeči je silně událost-řízený (DOM události). Příjem odpovědi Ajax není nic jiného než událost.
Obtíže by mohly vzniknout, pokud budete muset pracovat s kódem třetí strany, ale většina problémů může být vyřešena pouhým přemýšlením prostřednictvím aplikačního toku.
Promise API je nová funkce ECMAScript 6 (ES2015), ale má dobrou podporu prohlížeče již. Existuje také mnoho knihoven, které implementují standardní Promises API a poskytují další metody pro usnadnění použití a složení asynchronních funkcí (např. bluebird ).
Sliby jsou kontejnery pro budoucí hodnoty. Když slib obdrží hodnotu (je vyřešeno) nebo když je zrušeno (odmítnuto), oznámí všem svým "posluchačům", kteří chtějí k této hodnotě přistupovat.
Výhodou oproti prostému zpětnému volání je, že vám umožňují oddělit kód a je snazší je skládat.
Zde je jednoduchý příklad použití příslibu:
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).
});
Na výzvu Ajaxu bychom mohli použít takovéto sliby:
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
});
Popis všech výhod, které tato nabídka nabízí, je nad rámec této odpovědi, ale pokud píšete nový kód, měli byste je vážně zvážit. Poskytují velkou abstrakci a oddělení kódu.
Další informace o slibech: HTML5 skály - Sliby JavaScriptu
Odložené objekty jsou jQuery vlastní implementací slibů (před standardizací API Promise). Chová se téměř jako sliby, ale vystavuje mírně odlišné API.
Každá Ajax metoda jQuery již vrací "odložený objekt" (vlastně slib odloženého objektu), který můžete jednoduše vrátit z vaší funkce:
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
Mějte na paměti, že sliby a odložené objekty jsou jen kontejnery pro budoucí hodnotu nejsou hodnotou samotnou. Předpokládejme například, že jste měli následující:
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
}
Tento kód nepochopí výše uvedené problémy s asynchronií. Konkrétně $.ajax()
nezablokuje kód, když kontroluje stránku '/ password' na vašem serveru - pošle požadavek serveru a zatímco čeká, okamžitě vrátí objekt jQuery Ajax Deferred, nikoli odpověď ze serveru. To znamená, že příkaz if
bude vždy dostávat tento odložený objekt, bude jej považovat za true
a bude postupovat, jako by byl uživatel přihlášen.
Oprava je však jednoduchá:
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
});
Jak jsem zmínil, některé (!) Asynchronní operace mají synchronní protějšky. Nepodporuji jejich použití, ale pro úplnost je zde způsob, jakým byste provedli synchronní volání:
Pokud přímo použijete XMLHTTPRequest
object, předejte false
jako třetí argument do .open
.
Pokud použijete jQuery , můžete nastavit async
možnost false
. Všimněte si, že tato volba je zastaralé od jQuery 1.8. Potom můžete ještě použít success
zpětné volání nebo přistupovat k vlastnosti responseText
objektu jqXHR :
function foo() {
var jqXHR = $.ajax({
//...
async: false
});
return jqXHR.responseText;
}
Pokud používáte jinou metodu jQuery Ajax, jako je $.get
, $.getJSON
atd., Musíte ji změnit na $.ajax
(protože parametr $.ajax
můžete předávat pouze konfigurační parametry).
Hlavy nahoru! Není možné provést synchronní JSONP request. JSONP je svým charakterem vždy asynchronní (ještě jeden důvod, proč ani tuto možnost neuvažovat).
Váš kód by měl být něco jako toto:
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 udělal skvělou práci, když napsal odpověď pro lidi používající jQuery pro AJAX, rozhodl jsem se poskytnout alternativu pro lidi, kteří nejsou.
( Poznámka: pro ty, kteří používají nové fetch
API, Angular nebo sliby jsem přidal další odpověď níže )
Toto je krátké shrnutí "Vysvětlení problému" z druhé odpovědi, pokud si nejste jisti po přečtení tohoto článku, přečtěte si to.
Ain AJAX znamená asynchronní . To znamená, že odeslání požadavku (nebo spíše přijetí odpovědi) je vyřazeno z běžného prováděcího toku. Ve vašem příkladu .send
vrací okamžitě a další příkaz, return result;
, je proveden před tím, než byla funkce, kterou jste předali jako zpětné volání success
, vyvolána.
To znamená, že když se vrátíte, definovaný posluchač se ještě nespustil, což znamená, že hodnota, kterou vracíte, nebyla definována.
Zde je jednoduchá analogie
function getFive(){
var a;
setTimeout(function(){
a=5;
},10);
return a;
}
Hodnota a
je vrácena undefined
, protože část a=5
ještě nebyla provedena. AJAX se chová takto, vracíte hodnotu dříve, než server dostane šanci vašemu prohlížeči říct, jaká hodnota je.
Jedním z možných řešení tohoto problému je kód re-active, sdělením programu, co má dělat, když je výpočet dokončen.
function onComplete(a){ // When the code completes, do this
alert(a);
}
function getFive(whenDone){
var a;
setTimeout(function(){
a=5;
whenDone(a);
},10);
}
Toto se nazývá CPS . V podstatě předáváme getFive
akci, která se má provést, když je dokončena, říkáme našim kódům, jak reagovat, když je událost dokončena (jako naše volání AJAX nebo v tomto případě časový limit).
Použití by bylo:
getFive(onComplete);
Který by měl na obrazovce upozornit "5". (Fiddle) .
V podstatě existují dva způsoby, jak to vyřešit:
Pokud jde o synchronní AJAX, nedělejte to! Felixova odpověď vyvolává některé přesvědčivé argumenty o tom, proč je to špatný nápad. Chcete-li to shrnout, bude zmrazení prohlížeče uživatele, dokud server vrátí odpověď a vytvořit velmi špatné uživatelské zkušenosti. Zde je další stručné shrnutí z MDN o tom, proč:
XMLHttpRequest podporuje synchronní i asynchronní komunikaci. Obecně by však měly být upřednostňovány asynchronní požadavky před synchronními požadavky z důvodů výkonu.
Stručně řečeno, synchronní požadavky blokují provádění kódu ... ... což může způsobit vážné problémy ...
Pokud to uděláte mít, můžete předat příznak: Zde je návod:
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);
}
Nechte svou funkci přijmout zpětné volání. V příkladu může být kód foo
přijat, aby přijal zpětné volání. Náš kód vám řekne, jak reagovat když foo
dokončí.
Tak:
var result = foo();
// code that depends on `result` goes here
Stává se:
foo(function(result) {
// code that depends on `result`
});
Zde jsme prošli anonymní funkcí, ale mohli bychom stejně snadno předat odkaz na existující funkci, takže to vypadá takto:
function myHandler(result) {
// code that depends on `result`
}
foo(myHandler);
Pro více informací o tom, jak je tento způsob zpětného volání proveden, zkontrolujte Felixovu odpověď.
Nyní definujme foo, aby podle toho jednal
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();
}
Nyní jsme provedli naši funkci foo, která přijímá akci, která se spustí, když AJAX úspěšně dokončí, můžeme ji dále rozšířit kontrolou, zda stav odezvy není 200 a podle toho postupovat (vytvořit handler a podobně). Efektivní řešení našeho problému.
Pokud se vám stále nedaří pochopit tento přečtěte si návod [AJAX začínání průvodce na MDN.
XMLHttpRequest 2 (nejprve přečtěte si odpovědi z Benjamin Gruenbaum & Felix Kling )
Pokud nepoužíváte jQuery a chcete pěkný krátký XMLHttpRequest 2, který funguje na moderních prohlížečích a také v mobilních prohlížečích, doporučuji použít tento způsob:
function ajax(a, b, c){ // URL, callback, just a placeholder
c = new XMLHttpRequest;
c.open('GET', a);
c.onload = b;
c.send()
}
Jak můžete vidět:
Odpověď tohoto volání Ajax lze získat dvěma způsoby (tři pomocí jména XMLHttpRequest var):
Nejjednodušší:
this.response
Nebo pokud z nějakého důvodu bind()
zavoláte zpět do třídy:
e.target.response
Příklad:
function callback(e){
console.log(this.response);
}
ajax('URL', callback);
Nebo (výše uvedené je lepší anonymní funkce jsou vždy problém):
ajax('URL', function(e){console.log(this.response)});
Nic jednoduššího.
Někteří lidé budou pravděpodobně říkat, že je lepší použít onreadystatechange nebo dokonce proměnný název XMLHttpRequest. To je špatně.
Vyzkoušet Rozšířené funkce XMLHttpRequest
Podporoval všechny * moderní prohlížeče. A mohu potvrdit, že používám tento přístup, protože XMLHttpRequest 2 existuje. Nikdy jsem neměl žádný problém ve všech prohlížečích, které používám.
onreadystatechange je užitečné pouze v případě, že chcete získat záhlaví ve stavu 2.
Použití proměnné XMLHttpRequest
je další velkou chybou, protože je třeba provést zpětné volání uvnitř uzávěrů onload/oreadystatechange, jinak jste je ztratili.
Nyní, pokud chcete něco složitějšího pomocí Post a FormData, můžete tuto funkci snadno rozšířit:
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)
}
Opět ... je to velmi krátká funkce, ale dělá to.
Příklady použití:
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
Nebo předejte celý prvek formuláře (document.getElementsByTagName('form')[0]
):
var fd = new FormData(form);
x(url, callback, 'post', fd);
Nebo můžete nastavit některé vlastní hodnoty:
var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);
Jak vidíte, neimplementoval jsem synchronizaci ... je to špatná věc.
Řekl jsem, že ... proč to neudělat snadněji?
Jak je uvedeno v komentáři, použití chyby && synchrónně zcela porušuje bod odpovědi. Což je pěkný krátký způsob, jak použít Ajax správným způsobem?
Chyba obsluha
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);
Ve výše uvedeném skriptu máte obslužnou rutinu chyb, která je staticky definována, takže neohrožuje funkci. Popisovač chyb lze použít i pro jiné funkce.
Ale aby se opravdu dostalo chyby, je only way způsob, jak napsat špatnou adresu URL, v tomto případě každý prohlížeč vyvolá chybu.
Popisovače chyb jsou užitečné, pokud nastavíte vlastní záhlaví, nastavíte typ bufferu na buffer blob pole nebo cokoliv ...
Dokonce i když předáte 'POSTAPAPAP' jako metodu, nebude to chyba.
Dokonce i když předáte 'fdggdgilfdghfldj' jako formdata, nebude to chyba.
V prvním případě je chyba uvnitř displayAjax()
pod this.statusText
jako Method not Allowed
.
Ve druhém případě to prostě funguje. Pokud jste předali správná data, musíte zkontrolovat na straně serveru.
cross-domain není povoleno automaticky.
V chybové reakci nejsou žádné chybové kódy.
Existuje pouze this.type
, které je nastaveno na chybu.
Proč přidávat obslužný program chyb, pokud nemáte naprosto žádnou kontrolu nad chybami? Většina chyb se vrací zpět do funkce zpětného volání displayAjax()
.
Takže: Není třeba provádět kontroly chyb, pokud jste schopni kopírovat a vložit adresu URL správně. ;)
PS: Jako první test jsem napsal x ('x', displayAjax) ..., a to je úplně odpověď ... ??? Tak jsem zkontroloval složku, kde se nachází HTML, a tam byl soubor nazvaný 'x.xml'. Takže i když zapomenete rozšíření souboru XMLHttpRequest 2 WILL FIND IT. Já jsem LOL'd
Číst soubor synchronní
Nedělej to.
Pokud chcete prohlížeč na chvíli zablokovat, nahrajte soubor Nice big .txt
synchronní.
function omg(a, c){ // URL
c = new XMLHttpRequest;
c.open('GET', a, true);
c.send();
return c; // Or c.response
}
Teď můžete udělat
var res = omg('thisIsGonnaBlockThePage.txt');
Neexistuje žádný jiný způsob, jak to udělat neanynchronním způsobem. (Jo, s setTimeout smyčkou ... ale vážně?)
Dalším bodem je ... pokud pracujete s rozhraním API nebo jen s vlastními soubory v seznamu nebo s tím, co pro každou žádost používáte vždy různé funkce ...
Pouze v případě, že máte stránku, na kterou se načte vždy stejný XML/JSON nebo co potřebujete pouze jedna funkce. V takovém případě trochu upravte funkci Ajax a nahraďte b svou speciální funkcí.
Výše uvedené funkce jsou určeny pro základní použití.
Chcete-li funkci EXTEND ...
Ano můžeš.
Používám spoustu API a jedna z prvních funkcí, které jsem integroval do každé HTML stránky, je první funkcí Ajax v této odpovědi, pouze s GET ...
Ale s XMLHttpRequest 2 můžete udělat spoustu věcí:
Udělal jsem download manager (s použitím rozsahů na obou stranách s životopisem, filereaderem, souborovým systémem), různými konvertory obrazových konvertorů pomocí plátna, naplňováním databází web SQL pomocí base64images a mnohem více ... Ale v těchto případech byste měli vytvořit funkci pouze pro to účel ... někdy potřebujete blob, pole buffery, můžete nastavit záhlaví, přepsat mimetype a tam je mnohem více ...
Ale otázkou je, jak vrátit odpověď Ajax ... (Přidal jsem snadný způsob.)
To znamená AngularJS, jQuery (s odloženým), nativní nahrazení XHR (fetch), EmberJS, uložení BackboneJS nebo libovolnou knihovnu uzlů, která vrací sliby.
Váš kód by měl být něco jako toto:
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.
Felix Kling odvedl skvělou práci, když napsal odpověď pro lidi používající jQuery s zpětnými voláními pro AJAX. Mám odpověď na nativní XHR. Tato odpověď je pro obecné použití slibů buď na frontendu nebo backendu.
Model souběžnosti JavaScriptu v prohlížeči a na serveru s NodeJS/io.js je asynchronní a reaktivní.
Kdykoliv zavoláte metodu, která vrací slib, obslužné rutiny then
jsou vždy provedeny asynchronně - tj. po kódu pod nimi, který není v .then
handleru.
To znamená, že když vracíte data
popisovač then
, který jste definovali, se ještě nespustil. To znamená, že hodnota, kterou vracíte, nebyla nastavena na správnou hodnotu v čase.
Zde je jednoduchá analogie problému:
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
Hodnota data
je undefined
, protože část data = 5
ještě nebyla provedena. Pravděpodobně se provede za sekundu, ale do té doby je pro vrácenou hodnotu irelevantní.
Vzhledem k tomu, že se operace ještě nestala (AJAX, serverové volání, IO, časovač), vracíte hodnotu před tím, než žádost dostala šanci sdělit vašemu kódu, jaká hodnota je.
Jedním z možných řešení tohoto problému je kód re-active, sdělením programu, co má dělat, když je výpočet dokončen. Sliby to aktivně umožňují časovou (časově citlivou) povahou.
Slib je hodnota v čase. Sliby mají stav, začínají jako nevyřízené a nemají žádnou hodnotu a mohou se vyrovnat:
Slib může změnit pouze stavy jednou, po kterém bude navždy zůstat ve stejném stavu. then
handlery můžete připojit k slibům extrahovat jejich hodnotu a zpracovávat chyby. then
handlers umožňují zřetězení hovorů. Sliby jsou vytvořeny pomocí pomocí API, které je vrátí . Například, více moderní AJAX nahrazení fetch
nebo jQuery je $.get
návratové sliby.
Když zavoláme .then
na slib a return něco z toho - dostaneme slib pro zpracovaná hodnota. Pokud vrátíme další slib, dostaneme úžasné věci, ale pojďme držet koně.
Podívejme se, jak můžeme výše uvedenou otázku vyřešit sliby. Nejprve si ukážeme naše pochopení slibných stavů shora pomocí konstruktoru Promise pro vytvoření funkce zpoždění:
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);
});
}
Poté, co jsme převedli setTimeout na použití slibů, můžeme použít then
, aby bylo možné počítat:
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;
});
Namísto vrácení hodnota, které nemůžeme udělat kvůli modelu souběžnosti, v podstatě vracíme hodnotu wrapper za hodnotu, kterou můžeme rozbalit s then
. Je to jako krabice, kterou můžete otevřít pomocí then
.
To platí pro původní volání API, můžete:
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`
})
Tak to funguje stejně dobře. Dozvěděli jsme se, že nemůžeme vrátit hodnoty z již asynchronních volání, ale můžeme použít sliby a řetězit je k provedení zpracování. Nyní víme, jak vrátit odpověď z asynchronního volání.
ES6 zavádí generátory které jsou funkce, které se mohou vrátit do středu a pak obnovit bod, ve kterém byly. To je obvykle užitečné pro sekvence, například:
function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
yield 1;
yield 2;
while(true) yield 3;
}
Je funkce, která vrací iterátor přes posloupnost 1,2,3,3,3,3,....
, kterou lze iterovat. I když je to zajímavé samo o sobě a otevírá prostor pro mnoho možností, je zde jeden zajímavý případ.
Je-li sekvence, kterou produkujeme, posloupností akcí, nikoli čísel, můžeme tuto funkci kdykoliv pozastavit, kdykoli je akce vydána, a počkejte na ni, než funkci obnovíme. Takže místo posloupnosti čísel potřebujeme posloupnost budoucí hodnot - to je: slibuje.
Tento poněkud choulostivý, ale velmi silný trik nám umožňuje psát asynchronní kód synchronním způsobem. Existuje několik "běžců", kteří to dělají za vás, psaní je krátké řádky kódu, ale je nad rámec této odpovědi. Použiju zde Promise.coroutine
od Bluebirdu, ale existují i další balíčky jako co
nebo Q.async
.
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
});
Tato metoda vrací samotný slib, který můžeme konzumovat z jiných coroutines. Například:
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();
V ES7 je to dále standardizováno, existuje několik návrhů právě teď, ale ve všech z nich můžete slibovat await
. Toto je jen "cukr" (hezčí syntaxe) pro návrh ES6 výše přidáním klíčových slov async
a await
. Výše uvedený příklad:
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
}
To stále vrací slib jen to samé :)
Používáte Ajax nesprávně. Cílem není vrátit nic, ale místo toho předat data něčemu, co se nazývá funkce zpětného volání, která data zpracovává.
To je:
function handleData( responseData ) {
// Do what you want with the data
console.log(responseData);
}
$.ajax({
url: "hi.php",
...
success: function ( data, status, XHR ) {
handleData(data);
}
});
Vrácení čehokoliv v odesílacím obslužném programu nebude nic dělat. Namísto toho musíte data buď vypustit, nebo to, co chcete, provést přímo v rámci funkce úspěchu.
Nejjednodušším řešením je vytvořit funkci JavaScriptu a zavolat ji pro zpětné volání 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);
});
Pro lidi, kteří používají AngularJS , zvládne tuto situaci pomocí Promises
.
Zde říká,
Sliby mohou být použity k odemknutí asynchronních funkcí a umožňují jednořetězcové více funkcí dohromady.
Můžete najít pěkné vysvětlení zde také.
Příklad naleznete v dokumentech níže.
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.
V Angular2
se podívejte na následující příklad, ale jeho doporučeno použít Observables
s Angular2
.
search(term: string) {
return this.http
.get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
.map((response) => response.json())
.toPromise();
}
Tímto způsobem můžete konzumovat,
search() {
this.searchService.search(this.searchField.value)
.then((result) => {
this.result = result.artists.items;
})
.catch((error) => console.error(error));
}
Viz originál post zde. Ale TypeScript nepodporuje nativní es6 Promises , pokud ho chcete použít, možná budete potřebovat plugin pro to.
Navíc zde jsou sliby spec define.
Většina odpovědí zde poskytuje užitečné návrhy, když máte jednu asynchronní operaci, ale někdy to nastane, když potřebujete provést asynchronní operaci pro každý záznam v poli nebo jiné struktuře podobné seznamu. Je to pokušení:
// WRONG
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.Push(result);
});
});
console.log(results); // E.g., using them, returning them, etc.
Příklad:
// 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;
}
Důvodem, proč to nefunguje, je to, že zpětná volání z doSomethingAsync
ještě nebyly spuštěny v době, kdy se snažíte výsledky využít.
Takže pokud máte pole (nebo seznam nějakého druhu) a chcete provádět asynchronní operace pro každou položku, máte dvě možnosti: Proveďte operace paralelně (překrývající se) nebo v sériích (postupně po sobě).
Můžete spustit všechny z nich a sledovat, kolik zpětných volání očekáváte, a poté použít výsledky, když jste získali mnoho zpětných volání:
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
}
});
});
Příklad:
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;
}
(Mohli bychom se zbavit expecting
a použít results.length === theArray.length
, ale ponecháváme otevřenou možnost, že theArray
se změní, když jsou hovory nevyřízené ...)
Všimněte si, že používáme index
z forEach
k uložení výsledku do results
ve stejné pozici jako položka, ke které se vztahuje, a to i v případě, že výsledky dorazí mimo pořadí (protože asynchronní volání nemusí být nutně dokončena v pořadí, v jakém byla spuštěna) ).
Ale co když potřebujete návrat ty výsledky z funkce? Jak ukázali ostatní odpovědi, nemůžete; musíte mít svou funkci přijmout a zavolat zpětné volání (nebo vrátit Promise ). Zde je verze zpětného volání:
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);
});
Příklad:
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;
}
Nebo zde je verze, která vrací namísto 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);
});
Samozřejmě, pokud doSomethingAsync
nám prošly chyby, použijeme reject
k odmítnutí slibu, když se vyskytne chyba.)
Příklad:
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;
}
(Nebo můžete střídavě vytvořit obálku pro doSomethingAsync
, která vrací slib a poté proveďte níže uvedený postup ...)
Pokud doSomethingAsync
vám dává Promise , můžete použít Promise.all
:
function doSomethingWith(theArray) {
return Promise.all(theArray.map(function(entry) {
return doSomethingAsync(entry);
}));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Pokud víte, že doSomethingAsync
bude ignorovat druhý a třetí argument, můžete ho předat přímo do map
(map
zavolá zpětné volání se třemi argumenty, ale většina lidí používá první většinu času):
function doSomethingWith(theArray) {
return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
console.log("Results:", results);
});
Příklad:
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;
}
Všimněte si, že Promise.all
řeší svůj slib s řadou výsledků všech slibů, které mu dáte, když jsou všechny vyřešeny, nebo odmítá svůj slib, když první slibů, které mu sdělíte, odmítne.
Předpokládejme, že nechcete, aby operace byly paralelní? Chcete-li je spouštět jeden po druhém, musíte počkat na dokončení každé operace, než začnete další. Zde je příklad funkce, která tuto funkci provádí, a zavolá zpětné volání s výsledkem:
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);
});
(Protože děláme práci v seriálu, můžeme použít results.Push(result)
, protože víme, že výsledky nedostaneme mimo řádek. Ve výše uvedeném jsme mohli použít results[index] = result;
, ale v některých z následujících příkladů jsme don ' t mít index k použití.)
Příklad:
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;
}
(Nebo opět vytvořte obal pro doSomethingAsync
, který vám slibuje a udělejte níže ...)
Pokud doSomethingAsync
vám dá slib, pokud můžete použít syntaxi ES2017 + (snad s transpilerem jako Babel ), můžete použít funkci async
with for-of
a 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);
});
Příklad:
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;
}
Pokud nemůžete použít syntaxi ES2017 + (zatím), můžete použít variace na vzor - "Slib snížit" (to je složitější než obvyklé snížení slibů, protože výsledek nepřenášíme z jednoho do druhého). další, ale namísto toho shromažďování jejich výsledků v poli):
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);
});
Příklad:
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;
}
... což je méně těžkopádné s ES2015 + šipkami :
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);
});
Příklad:
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;
}
Podívejte se na tento příklad:
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);
});
});
Jak vidíte, getJoke
je vrací a resolved slib (je vyřešen při návratu res.data.value
). Takže počkejte, dokud nebude dokončeno $ http.get request a poté console.log (res.joke) (jako normální asynchronní tok).
Toto je plnkr:
http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/
Způsob ES6 (async - čeká)
(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);
});
})();
Další přístup k vrácení hodnoty z asynchronní funkce je předat objekt, který uloží výsledek z asynchronní funkce.
Zde je příklad:
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');
});
Používám objekt result
k uložení hodnoty během asynchronní operace. To umožňuje, aby byl výsledek dostupný i po asynchronní úloze.
Tento přístup používám hodně. Zajímalo by mě, jak dobře tento přístup funguje tam, kde je zapojeno zapojení výsledku zpět do modulů.
Toto je jedno z míst, které dvě cesty datové vazby to je použito v mnoha nových frameworkech JavaScriptu, které budou pro vás fungovat ...
Pokud tedy používáte Angular, React nebo jiné rámce, které dělají dva způsoby vazby dat, tento problém je pro vás jednoduše stanoven, takže ve snadném Wordu je váš výsledek undefined
na adrese první fáze, takže jste dostali result = undefined
dříve, než obdržíte data, pak jakmile se dostanete výsledek, bude aktualizován a dostat přiřazeny na novou hodnotu, která odpovídá na vaše volání Ajax ...
Ale jak to můžete udělat v čistém javascriptu nebo jQuery například, jak jste se ptali v této otázce?
Můžete použít zpětné volání , slib a nedávno pozorovatelný pro jeho zpracování, například v slibech máme nějakou funkci jako úspěch () nebo pak () který bude proveden, když jsou vaše data připravena pro vás, stejně jako callback nebo subscribe function on pozorovatelný .
Například ve vašem případě, který používáte jQuery , můžete něco takového udělat:
$(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
});
Pro více informací studujte o slibech a pozorovatelných což jsou novější způsoby, jak to provést asynchronně.
I když sliby a zpětná volání fungují v mnoha situacích dobře, je to bolest vzadu, aby vyjádřila něco jako:
if (!name) {
name = async1();
}
async2(name);
Měli byste skončit přes async1
; zkontrolujte, zda je name
nedefinováno nebo ne, a volejte odpovídajícím způsobem zpětné volání.
async1(name, callback) {
if (name)
callback(name)
else {
doSomething(callback)
}
}
async1(name, async2)
I když je okay v malých příkladech je to nepříjemné, když máte spoustu podobných případů a zpracování chyb.
Fibers
pomáhá při řešení problému.
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
}
Projekt můžete zkontrolovat zde .
Krátká odpověď je, musíte provést zpětné volání takto:
function callback(response) {
// Here you can do what ever you want with the response object.
console.log(response);
}
$.ajax({
url: "...",
success: callback
});
Následující příklad jsem napsal ukazuje, jak
Tento pracovní příklad je samostatný. Definuje jednoduchý objekt požadavku, který používá objekt XMLHttpRequest
okna k volání. Bude definovat jednoduchou funkci, která bude čekat na dokončení slibů.
Kontext. Příkladem je dotazování koncového bodu Spotify Web API s cílem vyhledat objekty playlist
pro danou sadu řetězců dotazů:
[
"search?type=playlist&q=%22Doom%20metal%22",
"search?type=playlist&q=Adele"
]
Pro každou položku nový slib zapálí blok - ExecutionBlock
, analyzuje výsledek, naplánuje novou sadu slibů založenou na poli výsledků, což je seznam objektů Spotify user
a provede nové volání HTTP v rámci ExecutionProfileBlock
asynchronně.
Potom můžete zobrazit vnořenou strukturu Promise, která vám umožní plodit více a zcela asynchronních vnořených volání HTTP a spojit výsledky z každé podmnožiny volání prostřednictvím Promise.all
.
NOTENedávné Spotify search
API budou vyžadovat token přístupu, který bude specifikován v hlavičkách požadavku:
-H "Authorization: Bearer {your access token}"
Chcete-li tedy spustit následující příklad, který chcete v hlavičkách požadavku umístit token přístupu:
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" />
Toto řešení jsem rozsáhle projednal zde .
To je velmi jednoduché:
Zde je pracovní verze kódu:
(async function(){
var response = await superagent.get('...')
console.log(response)
})()
čeká je podporováno ve všech aktuálních prohlížečích a uzlu 8
Tuto vlastní knihovnu (napsanou pomocí Promise) můžete použít pro vzdálené volání.
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);
};
});
}
Jednoduchý příklad použití:
$http({
method: 'get',
url: 'google.com'
}).then(function(response) {
console.log(response);
}, function(error) {
console.log(error)
});
Js je jeden závit.
Prohlížeč lze rozdělit do tří částí:
1) Event Loop
2) Web API
3) Fronta událostí
Event Loop běží pro navždy, tj. Druh nekonečné smyčky.Event Queue je místo, kde jsou všechny vaše funkce tlačeny na nějaké události (například: click) to je jeden po druhém provedené z fronty a umístěny do Event loop, který vykonává tuto funkci a připravuje ji na sebe To znamená, že spuštění jedné funkce nezačne, dokud se funkce ve frontě neprovede ve smyčce události.
Přemýšlejme, že jsme ve frontě posunuli dvě funkce, jedna je pro získání dat ze serveru a druhá využívá tato data. Přepnuli jsme funkci serverRequest () ve frontě a pak funkci utiliseData (). serverRequest funkce jde do smyčky událostí a provádí volání na server, protože nikdy nevíme, kolik času bude trvat, než se data ze serveru, takže tento proces se očekává, že čas, a tak jsme zaneprázdněni naší události smyčky tak visí naše stránky, to je místo, kde Web API přijde do role, že vezme tuto funkci z smyčky událostí a zabývá se serverem, který dělá smyčku událostí zdarma, takže můžeme vykonat další funkci z fronty. Další funkcí ve frontě je utiliseData (), která jde do smyčky, ale protože nejsou k dispozici žádná data, to jde odpad a provedení další funkce pokračuje až do konce fronty (toto se nazývá Async volání, tj. můžeme udělat něco jiného, než dostaneme data)
Předpokládejme, že naše funkce serverRequest () měla návratový kód v kódu, když získáme zpět data ze serveru Web API bude tlačit do fronty na konci fronty. Vzhledem k tomu, že se posunuje na konec ve frontě, nemůžeme využít jeho data, protože v naší frontě není žádná funkce pro využití těchto dat .Není tedy možné vrátit něco z Async Call.
Řešení tohoto problému je tedy {callback nebo slib.
Obrázek z jedné z odpovědí zde správně vysvětluje použití zpětného volání ... Dáváme funkci (funkce využívající data vrácená ze serveru) funkci volání serveru.
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);
}
V mém kódu se nazývá jako
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");
}
Přečtěte si zde nové metody v ECMA (2016/17) pro provádění asynchronního volání (@Felix Kling Odpověď nahoře) https://stackoverflow.com/a/14220323/7579856
Dalším řešením je provedení kódu přes sekvenční executor nsynjs .
nsynjs vyhodnotí postupně všechny sliby a výsledek slibuje do vlastnosti data
:
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>
Krok 1. Funkce Wrap s zpětným zavoláním do nsynjs-aware wrapperu (pokud má slibnou verzi, můžete tento krok přeskočit):
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;
Krok 2. Vložte synchronní logiku do funkce:
function process() {
console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}
Krok 3. Spusťte funkci synchronně přes nsynjs:
nsynjs.run(process,this,function () {
console.log("synchronous function finished");
});
Nsynjs vyhodnotí všechny operátory a výrazy krok za krokem, pozastavení provádění v případě, že výsledek nějaké pomalé funkce není připraven.
Další příklady zde: https://github.com/amaksr/nsynjs/tree/master/examples
Je to velmi běžný problém, kterému čelíme, když se potýkáme s „záhadami“ JavaScriptu. Dovolte mi zkusit toto tajemství dnes demystifikovat.
Začněme s jednoduchou funkcí JavaScriptu:
function foo(){
// do something
return 'wohoo';
}
let bar = foo(); // bar is 'wohoo' here
To je jednoduché synchronní volání funkce (kde každý řádek kódu je 'hotový s jeho prací' před příští v posloupnosti), a výsledek je stejný jak očekával.
Nyní přidáme trochu zkroucení tím, že v naší funkci zavedeme malé zpoždění, takže všechny řádky kódu nejsou „hotové“ v sekvenci. Bude tedy emulovat asynchronní chování funkce:
function foo(){
setTimeout( ()=>{
return 'wohoo';
}, 1000 )
}
let bar = foo() // bar is undefined here
Takže tam jdete, že zpoždění jen zlomilo funkčnost, kterou jsme očekávali! Ale co se přesně stalo? Je to vlastně docela logické, když se podíváte na kód. funkce foo()
, po provedení, nevrací nic (tedy vrácená hodnota je undefined
), ale spustí časovač, který provede funkci po 1s, aby se vrátil 'wohoo'. Ale jak vidíte, hodnota, která je přiřazena baru, je okamžitě vrácený materiál z foo (), ne nic jiného, co přijde později.
Jak tedy řešíme tento problém?
Zeptejte se naší funkce naSLIB. Slib je opravdu o tom, co to znamená: znamená to, že funkce vám zaručuje, že budete mít k dispozici jakýkoliv výstup, který se v budoucnu dostane. Podívejme se tedy na akci pro náš malý problém výše:
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'
});
Shrnutí je tedy - k řešení asynchronních funkcí, jako jsou volání na bázi iaxu atd., Můžete použít slib k resolve
hodnotě (kterou hodláte vrátit). Tedy, v krátkosti jste vyřešit hodnota namísto vracející se , v asynchronních funkcích.
Kromě použití then/catch
pro práci se sliby existuje ještě jeden přístup. Myšlenkou je rozpoznat asynchronní funkci a pak čekat na sliby vyřešit, před přesunem na další řádek kódu. Pod kapotou je stále jen promises
, ale s jiným syntaktickým přístupem. Pro lepší přehlednost můžete najít porovnání níže:
function fetchUsers(){
let users = [];
getUsers()
.then(_users => users = _users)
.catch(err =>{
throw err
})
return users;
}
async function fetchUsers(){
try{
let users = await getUsers()
return users;
}
catch(err){
throw err;
}
}
ECMAScript 6 má „generátory“, které umožňují snadno programovat v asynchronním stylu.
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();
}
}
Chcete-li spustit výše uvedený kód, postupujte takto:
const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function
Pokud potřebujete cílit prohlížeče, které nepodporují ES6, můžete kód spustit přes Babel nebo kompilátorem pro uzavření ECMAScript 5.
Zpětné volání ...args
jsou zabaleny do pole a jsou zničeny, když je čtete, aby se vzor mohl vyrovnat s zpětnými voláními, která mají více argumentů. Například s uzlem fs :
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
var App = App || {};
App = {
getDataFromServer: function(){
var self = this,
deferred = $.Deferred(),
requests = [];
requests.Push($.getJSON('request/ajax/url/1'));
requests.Push($.getJSON('request/ajax/url/2'));
$.when.apply(jQuery, requests).done(function(xhrResponse) {
return deferred.resolve(xhrResponse.result);
});
return deferred;
},
init: function(){
this.getDataFromServer().done(_.bind(function(resp1, resp2) {
// Do the operations which you wanted to do when you
// get a response from Ajax, for example, log response.
}, this));
}
};
App.init();
Krátká odpověď : Vaše foo()
metoda se okamžitě vrátí, zatímco volání $ajax()
provede asynchronně po návratu funkce . Problém je v tom, jak nebo kam uložit výsledky načtené asynchronním voláním, jakmile se vrátí.
V této niti bylo uvedeno několik řešení. Snad nejjednodušší způsob je předat objekt metodě foo()
a uložit výsledky v členu tohoto objektu po dokončení asynchronního volání.
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
Všimněte si, že volání foo()
vrátí nic užitečného. Výsledek asynchronního volání však bude nyní uložen v souboru result.response
.
Použijte funkci callback()
uvnitř foo()
úspěchu. Zkuste to takto. Je to jednoduché a snadno pochopitelné.
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();
Nacházíme se ve vesmíru, který vypadá, že postupuje podél dimenze, kterou nazýváme „čas“. Opravdu nechápeme, co je čas, ale vyvinuli jsme abstrakce a slovní zásobu, které nám daly důvod a hovořili o tom: "minulost", "přítomnost", "budoucnost", "před", "po".
Počítačové systémy, které stavíme - stále více - mají čas jako důležitý rozměr. Určité věci se připravují v budoucnu. Poté, co se tyto první věci nakonec objeví, se musí stát další věci. Toto je základní pojem nazvaný "asynchronicita". V našem stále více propojeném světě, nejběžnější případ asynchronicity čeká na nějaký vzdálený systém reagovat na nějaký požadavek.
Zvažte příklad. Zavoláte mlékař a objednejte si mléko. Když to přijde, chcete dát do své kávy. Teď si nemůžete dát mléko do kávy, protože tu ještě není. Budete muset počkat, až to přijde, než ji vložíte do kávy. Jinými slovy, následující nebude fungovat:
var milk = order_milk();
put_in_coffee(milk);
Protože JS nemá žádný způsob, jak vědět, že potřebuje počkat pro order_milk
skončí dříve, než provede put_in_coffee
. Jinými slovy, neví, že order_milk
je asynchronní - je něco, co nebude mít za následek mléko až do budoucna. JS a další deklarativní jazyky provádějí jedno prohlášení za druhým bez čekání.
Klasický JS přístup k tomuto problému, využívající skutečnosti, že JS podporuje funkce jako prvotřídní objekty, které mohou být předávány kolem, je předávání funkce jako parametru asynchronnímu požadavku, který pak vyvolá, když je dokončen. v budoucnu. To je přístup "zpětného volání". Vypadá to takto:
order_milk(put_in_coffee);
order_milk
zahajuje, objednává mléko, pak, když a jen když to přijde, vyvolá put_in_coffee
.
Problém s tímto přístupem zpětného volání je v tom, že znečišťuje normální sémantiku funkce vykazující její výsledek pomocí return
; funkce nesmí vykazovat své výsledky voláním zpětného volání zadaného jako parametr. Také tento přístup se může rychle chovat neprakticky, pokud se jedná o delší sekvence událostí. Řekněme například, že chci počkat, až bude mléko vloženo do kávy, a pak teprve poté provést třetí krok, konkrétně pití kávy. Nakonec musím napsat něco takového:
order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }
kde přecházím na put_in_coffee
jak mléko, které se do něj vloží, tak i akce (drink_coffee
), která se má provést, jakmile je mléko vloženo. Takový kód je obtížné psát a číst a ladit.
V tomto případě bychom mohli kód v otázce přepsat takto:
var answer;
$.ajax('/foo.json') . done(function(response) {
callback(response.data);
});
function callback(data) {
console.log(data);
}
To byla motivace k pojmu "slib", což je určitý typ hodnoty, který představuje budoucí nebo asynchronní výsledek nějakého druhu. Může to představovat něco, co se již stalo, nebo se to stane v budoucnu, nebo se nikdy nestane vůbec. Sliby mají jedinou metodu, pojmenovanou then
, na kterou předáváte akci, která má být provedena, když výsledek, který slib představuje, byl realizován.
V případě našeho mléka a kávy navrhujeme order_milk
vrátit slib pro přijíždějící mléko, poté specifikujeme put_in_coffee
jako akci then
takto:
order_milk() . then(put_in_coffee)
Jednou z výhod tohoto je, že můžeme tyto řetězce spojit a vytvořit sekvence budoucích výskytů („řetězení“):
order_milk() . then(put_in_coffee) . then(drink_coffee)
Pojďme aplikovat sliby na váš konkrétní problém. Logiku požadavků zabalíme do funkce, která vrací slib:
function get_data() {
return $.ajax('/foo.json');
}
Vlastně jsme přidali return
do volání $.ajax
. To funguje, protože $.ajax
jQuery již vrací podobnou věc. (V praxi, bez toho, abychom se dostali do podrobností, bychom raději zabalili toto volání tak, aby se vrátil skutečný slib, nebo použijte nějakou alternativu k $.ajax
, která tak dělá.) Nyní, pokud chceme soubor načíst a čekat na jeho dokončení a pak něco udělat, můžeme jednoduše říci
get_data() . then(do_something)
například,
get_data() .
then(function(data) { console.log(data); });
Když používáme sliby, skončíme předáváním mnoha funkcí do then
, takže je často užitečné používat kompaktnější funkce šipek ve stylu ES6:
get_data() .
then(data => console.log(data));
async
Ale stále je tu něco, co je nejistě nespokojené, že je třeba napsat kód jedním způsobem, pokud je synchronní a zcela odlišný, pokud je asynchronní. Pro synchronní píšeme
a();
b();
ale pokud je a
asynchronní, slibujeme, že musíme napsat
a() . then(b);
Nahoře jsme řekli: „JS nemá žádný způsob, jak vědět, že potřebuje počkat pro první volání, které bude trvat dříve, než provede druhé“. Nebylo by hezké, kdyby tam byl nějaký způsob, jak říct JS, že? Ukazuje se, že existuje klíčové slovo await
, které se používá uvnitř speciálního typu funkce nazvané "asynchronní" funkce. Tato funkce je součástí připravované verze ES, ale je již k dispozici v transpilerech, jako je Babel, s danými správnými předvolbami. To nám umožňuje jednoduše psát
async function morning_routine() {
var milk = await order_milk();
var coffee = await put_in_coffee(milk);
await drink(coffee);
}
Ve vašem případě byste mohli něco napsat
async function foo() {
data = await get_data();
console.log(data);
}
Samozřejmě existuje mnoho přístupů, jako je synchronní požadavek, slib, ale podle mých zkušeností bych měl použít přístup zpětného volání. Je přirozené asynchronní chování Javascriptu. Takže fragment kódu může být přepsán trochu jinak:
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
myCallback(response);
}
});
return result;
}
function myCallback(response) {
// Does something.
}
Otázkou bylo:
Jak vrátím odpověď z asynchronního volání?
které lze interpretovat jako:
Jak udělat asynchronní code look synchronní?
Řešením bude vyhnout se zpětným voláním a použít kombinaci Promises a async/await.
Chtěl bych uvést příklad pro žádost Ajax.
(Ačkoli to může být psáno v Javascript, já dávám přednost psát to v Python, a kompilovat to k Javascript používat Transcrypt . To bude jasné dost.)\T
Umožňuje nejprve povolit použití JQuery, aby $
bylo k dispozici jako S
:
__pragma__ ('alias', 'S', '$')
Definujte funkci, která vrátí Promise, v tomto případě volání 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()
Použijte asynchronní kód, jako kdyby byl synchronní:
async def readALot():
try:
result1 = await read("url_1")
result2 = await read("url_2")
except Exception:
console.warn("Reading a lot failed")
Pomocí ES2017 byste měli mít tuto funkci jako deklaraci funkce
async function foo() {
var response = await $.ajax({url: '...'})
return response;
}
A takhle.
(async function() {
try {
var result = await foo()
console.log(result)
} catch (e) {}
})()
Nebo syntaxe Promise
foo().then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
Spíše než házet kód na vás, tam jsou 2 pojmy, které jsou klíčem k pochopení, jak JS zpracovává zpětná volání a asynchronicity. (je to i slovo?)
Musíte mít na paměti tři věci; Fronta; smyčka události a zásobník
V širokých, zjednodušujících termínech je smyčka událostí jako projektový manažer, neustále naslouchá všem funkcím, které chtějí běžet, a komunikuje mezi frontou a stackem.
while (queue.waitForMessage()) {
queue.processNextMessage();
}
Jakmile přijme zprávu, spustí něco, co ji přidá do fronty. Fronta je seznam věcí, které čekají na provedení (jako vaše AJAX požadavek). představte si to takto:
1. call foo.com/api/bar using foobarFunc
2. Go perform an infinite loop
... and so on
Když se jedna z těchto zpráv provede, objeví se zpráva z fronty a vytvoří se zásobník, zásobník je vše, co JS potřebuje k provedení instrukce ve zprávě. Takže v našem příkladu se říká, že má volat foobarFunc
function foobarFunc (var) {
console.log(anotherFunction(var));
}
Takže cokoliv, co foobarFunc potřebuje provést (v našem případě anotherFunction
), se dostane do zásobníku. po spuštění a poté zapomenutí - smyčka události se pak přesune na další věc ve frontě (nebo si poslechne zprávy)
Klíčovou věcí je pořadí provedení. To je
Když zavoláte pomocí AJAX na externí stranu nebo spustíte jakýkoli asynchronní kód (například setTimeout), Javascript závisí na odezvě, než bude pokračovat.
Velkou otázkou je, kdy dostane odpověď? Odpověď zní, že nevíme - takže smyčka událostí čeká na to, že zpráva řekne "hej běž mě". Pokud JS jen čekal na tuto zprávu synchronně vaše aplikace by zmrazit a bude sát. JS tak pokračuje ve vykonávání další položky ve frontě, zatímco čeká, až se zpráva vrátí zpět do fronty.
Proto s asynchronními funkcemi používáme věci nazývané zpětné volání . Je to trochu jako slibovat doslova. Stejně jako v I slibuji, že něco vrátím jQuery používá specifická zpětná volání nazvaná deffered.done
deffered.fail
a deffered.always
mimo jiné).
Takže to, co musíte udělat, je předat funkci, která je slíbena provést v určitém okamžiku s daty, které jsou předány na něj.
Protože zpětné volání není provedeno okamžitě, ale později je důležité předat odkaz na funkci, která nebyla provedena. tak
function foo(bla) {
console.log(bla)
}
tak většinu času (ale ne vždy) projdete foo
ne foo()
Doufejme, že to bude dávat smysl. Když se setkáte s takovými věcmi, které se zdají matoucí - vřele doporučuji, abyste si dokumentaci plně přečetli, abyste tomu alespoň porozuměli. Bude to mnohem lepší vývojář.
Podívejme se nejprve na les, než se podíváme na stromy.
Existuje mnoho informativních odpovědí s velkými detaily, nebudu opakovat žádné z nich. Klíčem k programování v JavaScriptu je nejprve správný mentální model celkového provedení.
Dobrou zprávou je, že pokud tomuto bodu dobře porozumíte, nikdy se nebudete muset starat o podmínky závodu. Měli byste v první řadě především, jak chcete kód zorganizovat jako v podstatě odpověď na různé diskrétní události a jak je chcete spojit do logické posloupnosti. Můžete použít sliby nebo vyšší úroveň async/await jako nástroje k tomuto účelu, nebo můžete použít svůj vlastní.
Ale neměli byste používat žádné taktické nástroje k vyřešení problému, dokud nebudete spokojeni se skutečnou problémovou doménou. Nakreslete mapu těchto závislostí, abyste věděli, co je třeba spustit, když. Pokus o ad-hoc přístup ke všem těmto zpětným voláním vám prostě nebude sloužit.
Nejdokonalejší odpověď na tuto otázku je použití Promise
.
function ajax(method, url, params) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open(method, url);
xhr.send(params);
});
}
ajax("GET", "/test", "acrive=1").then(function(result) {
// Code depending on result
})
.catch(function() {
// An error occurred
});
Existuje problém s použitím slibů!
Použil jsem toto řešení na chvíli, než jsem přišel na to, že je chyba ve starých prohlížečích:
Uncaught ReferenceError: Promise is not defined
Rozhodl jsem se tedy implementovat vlastní třídu Promise pro ES3 na níže js kompilátory, pokud nejsou definovány. Stačí přidat tento kód před hlavní kód a pak bezpečnostní uživatel Promise!
if(typeof Promise === "undefined"){
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Promise = function () {
function Promise(main) {
var _this = this;
_classCallCheck(this, Promise);
this.value = undefined;
this.callbacks = [];
var resolve = function resolve(resolveValue) {
_this.value = resolveValue;
_this.triggerCallbacks();
};
var reject = function reject(rejectValue) {
_this.value = rejectValue;
_this.triggerCallbacks();
};
main(resolve, reject);
}
Promise.prototype.then = function then(cb) {
var _this2 = this;
var next = new Promise(function (resolve) {
_this2.callbacks.Push(function (x) {
return resolve(cb(x));
});
});
return next;
};
Promise.prototype.catch = function catch_(cb) {
var _this2 = this;
var next = new Promise(function (reject) {
_this2.callbacks.Push(function (x) {
return reject(cb(x));
});
});
return next;
};
Promise.prototype.triggerCallbacks = function triggerCallbacks() {
var _this3 = this;
this.callbacks.forEach(function (cb) {
cb(_this3.value);
});
};
return Promise;
}();
}
Zde je příklad, který funguje:
const validateName = async userName => {
const url = "abc/xyz";
try {
const response = await axios.get(url);
return response.data
} catch (err) {
return false;
}
};
validateName("user")
.then(data => console.log(data))
.catch(reason => console.log(reason.message))