JavaScript Promise API

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!