Rozhraní JavaScript Promise API je úžasné, ale může být úžasné pomocí async
a await
!
I když se synchronní kód snáze sleduje a ladí, asynchronní je obecně lepší z hlediska výkonu a flexibility. Proč "zdržovat show", když můžete spustit mnoho požadavků najednou a pak je vyřídit, když je každý připraven? Promises se stávají velkou součástí světa JavaScriptu a mnoho nových rozhraní API je implementováno s filozofií slibů. Pojďme se podívat na sliby, rozhraní API, jak se používá!
Sliby v divočině
Rozhraní XMLHttpRequest API je asynchronní, ale není použijte Promises API. Existuje však několik nativních rozhraní API, která nyní používají sliby:
- Battery API
- fetch API (náhrada XHR)
- ServiceWorker API (příspěvek již brzy!)
Sliby budou stále převládat, takže je důležité, aby si na ně všichni vývojáři frontendu zvykli. Za zmínku také stojí, že Node.js je další platforma pro Promises (samozřejmě, protože Promise je základní jazyková funkce).
Testování slibů je pravděpodobně jednodušší, než si myslíte, protože setTimeout
lze použít jako váš asynchronní „úkol“!
Použití základního slibu
new Promise()
konstruktor by se měl používat pouze pro starší asynchronní úlohy, jako je použití setTimeout
nebo XMLHttpRequest
. Nový slib je vytvořen s new
klíčové slovo a příslib poskytuje resolve
a reject
funkce na poskytnuté zpětné volání:
var p = new Promise(function(resolve, reject) { // Do an async task async task and then... if(/* good condition */) { resolve('Success!'); } else { reject('Failure!'); } }); p.then(function(result) { /* do something with the result */ }).catch(function() { /* error :( */ }).finally(function() { /* executes regardless or success for failure */ });
Je na vývojáři, aby ručně zavolal resolve
nebo reject
v těle zpětného volání na základě výsledku jejich daného úkolu. Realistickým příkladem by bylo převedení XMLHttpRequest na úlohu založenou na slibech:
// From Jake Archibald's Promises and Back: // http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest function get(url) { // Return a new promise. return new Promise(function(resolve, reject) { // Do the usual XHR stuff var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // This is called even on 404 etc // so check the status if (req.status == 200) { // Resolve the promise with the response text resolve(req.response); } else { // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error(req.statusText)); } }; // Handle network errors req.onerror = function() { reject(Error("Network Error")); }; // Make the request req.send(); }); } // Use it! get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
Někdy to nepotřebujete dokončit asynchronní úkoly v rámci slibu – pokud je to možné že bude provedena asynchronní akce, nejlepší však bude vrácení slibu, abyste se vždy mohli spolehnout na to, že z dané funkce vypadne slib. V takovém případě můžete jednoduše zavolat na Promise.resolve()
nebo Promise.reject()
bez použití new
klíčové slovo. Například:
var userCache = {}; function getUserDetail(username) { // In both cases, cached or not, a promise will be returned if (userCache[username]) { // Return a promise without the "new" keyword return Promise.resolve(userCache[username]); } // Use the fetch API to get the information // fetch returns a promise return fetch('users/' + username + '.json') .then(function(result) { userCache[username] = result; return result; }) .catch(function() { throw new Error('Could not find user: ' + username); }); }
Protože příslib je vždy vrácen, můžete vždy použít then
a catch
metody na jeho návratovou hodnotu!
pak
Všechny instance příslibu dostanou then
metoda, která vám umožní reagovat na slib. První then
metoda callback přijme výsledek, který jí byl přidělen kódem resolve()
zavolejte:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 3000); }) .then(function(result) { console.log(result); }); // From the console: // 10
then
zpětné volání se spustí, když je příslib vyřešen. Můžete také řetězit then
metoda zpětných volání:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 3000); }) .then(function(num) { console.log('first then: ', num); return num * 2; }) .then(function(num) { console.log('second then: ', num); return num * 2; }) .then(function(num) { console.log('last then: ', num);}); // From the console: // first then: 10 // second then: 20 // last then: 40
Každý then
obdrží výsledek předchozího then
návratová hodnota 's.
Pokud byl příslib již vyřešen, ale then
je voláno znovu, okamžitě se spustí zpětné volání. Pokud je příslib odmítnut a vy zavoláte na then
po odmítnutí se zpětné volání nikdy nevolá.
chytit
catch
zpětné volání se provede, když je příslib odmítnut:
new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { reject('Done!'); }, 3000); }) .then(function(e) { console.log('done', e); }) .catch(function(e) { console.log('catch: ', e); }); // From the console: // 'catch: Done!'
Co poskytnete na reject
metoda je na vás. Častým vzorem je odesílání Error
na catch
:
reject(Error('Data could not be found'));
konečně
Nově představený finally
zpětné volání je voláno bez ohledu na úspěch nebo neúspěch:
(new Promise((resolve, reject) => { reject("Nope"); })) .then(() => { console.log("success") }) .catch(() => { console.log("fail") }) .finally(res => { console.log("finally") }); // >> fail // >> finally
Promise.all
Přemýšlejte o načítání JavaScriptu: jsou chvíle, kdy spustíte několik asynchronních interakcí, ale chcete reagovat až po dokončení všech – to je místo Promise.all
přichází. Promise.all
metoda vezme řadu slibů a spustí jedno zpětné volání, jakmile budou všechny vyřešeny:
Promise.all([promise1, promise2]).then(function(results) { // Both promises resolved }) .catch(function(error) { // One or more promises was rejected });
Perfektní způsob, jak přemýšlet o Promise.all
spouští více AJAX (přes fetch
) požaduje najednou:
var request1 = fetch('/users.json'); var request2 = fetch('/articles.json'); Promise.all([request1, request2]).then(function(results) { // Both promises done! });
Můžete kombinovat API jako fetch
a Battery API, protože oba vracejí sliby:
Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) { // Both promises done! });
Vyrovnat se s odmítnutím je samozřejmě těžké. Pokud je jakýkoli příslib odmítnut, catch
vystřelí pro první odmítnutí:
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 4000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { reject('Second!'); }, 3000); }); Promise.all([req1, req2]).then(function(results) { console.log('Then: ', results); }).catch(function(err) { console.log('Catch: ', err); }); // From the console: // Catch: Second!
Promise.all
bude velmi užitečné, protože se více rozhraní API posunuje směrem ke slibům.
Promise.race
Promise.race
je zajímavá funkce – namísto čekání na vyřešení nebo zamítnutí všech slibů, Promise.race
spustí se, jakmile je jakýkoli příslib v poli vyřešen nebo odmítnut:
var req1 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('First!'); }, 8000); }); var req2 = new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve('Second!'); }, 3000); }); Promise.race([req1, req2]).then(function(one) { console.log('Then: ', one); }).catch(function(one, two) { console.log('Catch: ', one); }); // From the console: // Then: Second!
Případem použití může být spuštění požadavku na primární zdroj a sekundární zdroj (v případě, že primární nebo sekundární zdroj není k dispozici).
Zvykněte si na sliby
Sliby byly žhavým tématem posledních několika let (nebo posledních 10 let, pokud jste byli uživatelem sady nástrojů Dojo) a ze vzoru rámce JavaScriptu se staly jazykové základy. Pravděpodobně je moudré předpokládat, že uvidíte většinu nových rozhraní JavaScript API implementovaných se vzorem založeným na slibech...
...a to je skvělá věc! Vývojáři se mohou vyhnout peklu zpětného volání a asynchronní interakce mohou být předávány jako jakákoli jiná proměnná. Slibům nějakou dobu trvá, než si zvyknou, že nástroje (přirozeně) existují a teď je čas se je naučit!