Funkcje async
oraz słowo kluczowe await
są częścią es8 (es2017). Nie są dostępne we wszystkich przeglądarkach,
chociaż ich wsparcie jest bardzo duże. Jeśli jesteś zainteresowany w jaki sposób Babel konwertuje async..await
, aby
przeglądarki, które ich nie obsługują mogły uruchomić ten kod, to ten wpis jest dla Ciebie. Kod ten wygląda jak
jeden do jeden dlatego pomyślałem, że warto o tym napisać.
Generatory to część es6 czyli es2015 i właśnie za ich pomocą Babel tworzy funkcje, które w oryginale używają słowa
kluczowego async
. Udostępnia też kod tzw. regenerator dla kodu, który korzysta z generatorów (jeśli nie zmusimy
go aby tego nie robił), ale my nie będziemy się nim tutaj zajmować.
Poniżej prosta funkcja asynchroniczna, korzystająca z async..await
, która zwraca tytuł pliku RSS, dla strony
Głównie JavaScript (blog używa CORS, więc można użyć tego
kodu też na innych stronach np. z CodePen, link na końcu):
async function title() {
var res = await fetch('https://jcubic.pl/feed.xml');
var text = await res.text();
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(text, "text/xml");
return xmlDoc.querySelector('title').innerHTML;
}
Ta sama funkcja jako generator wygląda tak:
function* title() {
var res = yield fetch('https://jcubic.pl/feed.xml');
var text = yield res.text();
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(text, "text/xml");
return xmlDoc.querySelector('title').innerHTML;
}
Jedyna różnica to gwiazdka po słowie function
zamiast słowa async
oraz słowo yield
zamiast await
.
Aby ten kod zadziałał, możemy wykorzystać ciekawą właściwość iteratorów (czyli niskopoziomowego api, które kryje są
za generatorami), a mianowicie możemy przekazać wartość, do następnego wywołania funkcji next
iteratora i ta
wartość zostanie zwrócona przez słowo kluczowe yield
, kiedy iterator wznowi swoje działanie.
Funkcja która przetworzy generator i zwróci obietnice wartości, jaką zwraca oryginalna funkcja, wygląda tak:
// funkcja zwraca true dla zwykłych obietnic jak i obiektów,
// które wyglądają jak obietnice, jak np. jQuery Defered
function is_promise(value) {
return value instanceof Promise || (typeof value === 'object' && typeof value.then === 'function');
}
function unwind(gen) {
// pobieramy iterator z generatora
var iterator = gen()[Symbol.iterator]();
return new Promise(function(resolve) {
(function next(value) {
// przekazujemy poprzednią wartość do następnego next
value = iterator.next(value);
if (!value.done) {
if (is_promise(value.value)) {
// wartość value z funkcji next to będzie już wartość obietnicy,
// a nie sama obietnica
value.value.then(next);
} else {
// zwykła wartość - raczej nie użyjemy async dla takich wartości
// dlatego nie powinno wystąpić ale nic nie stoi na przeszkodzie
// aby wywołac var answer = await 42;
next(value.value);
}
} else {
// nasza obietnica dostanie wartość, która zostaje zwrócona
// przez return oryginalnej funkcji
resolve(value.value);
}
})();
});
}
A tutaj jak wywołać funkcje unwind wraz z generatorem:
unwind(title).then((title) => console.log(title));
Na koniec moje demko na CodePen, które zawiera kod z tego wpisu.
Komentarze
Hasło, które podasz umożliwi ponowne zalogowanie się i np. usunięcie komentarza, jest dobrowolne. Email jest szyfrowany i używany do wysyłania powiadomień o odpowiedziach do komentarzy oraz do pobierania awatara dzięki usłudze Gravatar.com.