Promisy
V tomto článku se dozvíte co jsou to Promisy a jak je používat. Díky nim se vyhnete vnořování callbacků a váš kód bude čitelnější.
Co je to Promise a jak ho vytvořit
Promise je objekt, který bude reprezentovat dokončení nebo selhání nějaké asynchronní operace. Do češtiny slovo promise přeložíme jako slib. Promise můžeme vytvořit pomocí konstrutoru Promise, kterému jako parametr předáme funkci, která přijímá dva parametry. Tyto parametry jsou v podstatě funkce, takže je potom v předané funkci můžeme zavolat. Pokud zavoláme první argument, tak jsme Promise splnili (splnili jsme slib), a pokud zavoláme druhý argument tak jsme Promise nesplnili (nesplnili jsme slib).
Promise nám poskytuje dvě metody které se jmenují then a catch. Pomocí metody then můžeme nastavit funkci, která se zavolá když se Promise splní a pomocí metody catch můžeme nastavit funkci, která se zavolá když se Promise nesplní.
let pujduBehat = new Promise((resolve, reject) => {
let rand = Math.random();
if (rand > 0.5) {
resolve(); // splnění Promisu
} else {
reject(); // nesplnění Promisu
}
});
// nastavíme funkci, která se zavolá když se Promise splní
pujduBehat.then(() => {
console.log("jdu běhat");
});
// nastavíme funkci, která se zavolá když se Promise nesplní
pujduBehat.catch(() => {
console.log("nejdu běhat");
});
Předávání argumentů při splnění nebo nesplnění Promisu
Pokud chceme, tak můžeme při splnění nebo nesplnění Promisu předávat také nějaké argumenty. Následující ukázka ukazuje jak to provést.
let rand = Math.random();
if (rand > 0.5) {
resolve(rand); // předání argumentu při splnění Promisu
} else {
reject("něco se pokazilo"); // předání argumentu při nesplění Promisu
}
});
mujPromise.then((cislo) => {
console.log("Promise byl splněn, hodnota: " + cislo);
});
mujPromise.catch((chyba) => {
console.log("Promise nebyl splněn: " + chyba);
});
Řetězení metod
Vzhledem k tomu že funkce then vrací Promise, můžeme metodu then a catch zřetězit dohromady.
let rand = Math.random();
if (rand > 0.5) {
resolve();
} else {
reject();
}
});
pujduBehat.then(() => {
console.log("jdu běhat");
})
.catch(() => {
console.log("nejdu běhat");
});
To jakou hodnotu metoda then vrátí záleží na tom jestli něco ve funkci, kterou jí předáváme vrátíme. O tom co metoda then vrátí v různých případech se můžete dočíst níže. Teď nám bude stačit vědět že když nevrátíme nic, tak se vrátí splněný Promise, a když vrátíme nový Promise, tak se vrátí splněný nebo nesplněný Promise, podle toho jestli se Promise splní nebo nesplní. Můžeme tedy když chceme na nějakou asynchronní operaci navázat jinou asynchronní operací v metodě then vrátit nový Promise a zřetězit další metodu then. Lépe to pravděpodobně pochopíte v následující ukázce. A protože Promise v naprosté většině případů nebudeme jen tak vytvářet ale vrátí nám jej nějaká funkce, tak je v následující ukázce vytvořena funkce, která vrací Promise.
return new Promise((resolve, reject) => {
let cas = 500 + (Math.random() * 1000);
setTimeout(() => {
resolve();
}, cas);
});
}
pockejChvili()
.then(() => {
console.log("první");
return pockejChvili();
})
.then(() => {
console.log("druhý");
return pockejChvili();
})
.then(() => {
console.log("třetí");
return pockejChvili();
})
.catch(() => { // u tady toho přkladu metodu catch ani nepotřebujeme (i přesto jsem ji sem ale napsal)
console.log("chyba");
});
Metoda then
Metodě then předáváme jako parametr funkci, která se zavolá když se Promise splní. Ještě jí ale můžeme jako druhý parametr předat funkci, která se zavolá když se promise nesplní. Většinou ale budeme předávat jen funkci, která se zavolá když se Promise splní, tu druhou ve většině případů nepotřebujeme.
let rand = Math.random();
if (rand > 0.5) {
resolve();
} else {
reject();
}
});
mujPromise.then(() => { // funkce, která se zavolá když se Promise splní
console.log("Promise splněn");
}, () => { // funkce, která se zavolá když se Promise nesplní
console.log(Promise nebyl splněn");
});
návratová hodnota metody then
Návratová hodnota metody then je ovlivněna tím, jestli něco v předávané funkci vrátíme nebo ne. Existuje pár pravidel, která určují jakou hodnotu metoda then vrátí. Tato pravidla se týkají funkce, která se volá když se Promise splní i funkce, která se volá když se Promise nesplní.
- pokud předaná funkce vrátí nějakou hodnotu, tak se vrátí splněný Promise s vrácenou hodnotou funkce
- pokud předaná funkce nevrátí žádnou hodnotu, tak se vrátí splněný Promise
- pokud předaná funkce vyhodí chybu, tak se vrátí nesplněný Promise s vyhozenou chybou jako hodnotou
- pokud předaná funkce vrátí již splněný Promise, tak se vrátí splněný Promise, který bude mít stejnou hodnotu jako Promise vrácený předanou funkcí
- pokud předaná funkce vrátí již nesplněný Promise, tak se vrátí nesplněný Promise, který bude mít stejnou hodnotu jako Promise vrácený předanou funkcí
- pokud předaná funkce vrátí Promise čekající na splnění, tak se počká až se Promise splní nebo nesplní a poté funkce then vrátí Promise, který bude stejný jako Promise, který předaná funkce vrátila
Metoda catch
Metodě catch předáváme jako parametr funkci, která se zavolá když se Promise nesplní. V podstatě se metoda catch chová stejně jako metoda then, s tím rozdílem že přijímá jen funkci, která se zavolá, když se Promise nesplní. Takže metoda catch také vrací Promise a platí pro ni stejná pravidla, která jsou uvedena u metody then. Chová se stejně jako bychom zavolali metodu then takto: ".then(undefined, () => { /* ... */ })".
let rand = Math.random();
if (rand > 0.5) {
resolve();
} else {
reject();
}
});
mujPromise
.catch(() => { // metodu catch můžeme zavolat i před zavoláním metody then, metoda catch ale vrací splněný Promise, takže se volá až na konci
console.log("Promise nebyl splněn");
})
.then(() => { // tato metoda then se týká jen Promisu vráceného funkcí catch
// metoda catch vrací splněný Promise, takže se provede i tento kód (i když se třeba předaná funkce metody catch neprovede)
console.log("Promise splněn");
});
Metoda finally
Kromě metod then a catch máme ještě k dispozici metodu finally. Této metodě můžeme předat funkci, která se zavolá když se Promise splní i nesplní. Můžeme se tedy použitím této funkce vyhnout zduplikovanému kódu v metodách then a catch pokud potřebujeme aby se nějaký kód spustil při splnění i nesplnění Promisu.
let rand = Math.random();
if (rand > 0.5) {
resolve();
} else {
reject();
}
});
mujPromise
.then(() => {
console.log("Promise splněn");
})
.catch(() => {
console.log("Promise nebyl splněn");
})
.finally(() => {
console.log("funkce předaná metodě finally se zavolá vždy");
});
Statické Promise metody
Zde jsem vám vypsal všechny statické metody týkající se Promisů. Myslím že je ale pravděpodobně moc potřebovat nebudete.
Promise.all
Tato metoda bere jako parametr Iterable objekt (třeba pole) s Promisy a vrací Promise, který se splní když se splní všechny předané Promisy a nesplní když se alespoň jeden předaný promise nesplní. Jako hodnotu bude tento Promise mít pole hodnot všech předaných promisů.
resolve(10);
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 20); // za půl sekundy zavolá funkci resolve s argumentem 20
});
let promise3 = new Promise((resolve, reject) => {
let rand = Math.random();
if (rand > 0.5) {
resolve(30);
console.log("promise3 se splnil");
} else {
reject();
console.log("promise3 se nesplnil");
}
});
Promise.all([promise1, promise2, promise3]) // pokud všechny tři
.then((hodnoty) => {
console.log("všechny tři Promisy se splnily");
console.log("hodnoty všech Promisů:");
console.log(hodnoty);
});
Promise.any
Tato metoda bere jako parametr Iterable objekt (třeba pole) s Promisy a vrací Promise, který se splní když se alespoň jeden z předaných Promisů splní. Hodnota tohoto Promisu bude stejná, jako hodnota Promisu, který se jako první splnil. Pokud se nesplní žádný z předaných Promisů, tak se vrátí nesplněný Promise, který bude mít jako hodnotu AggregateError.
let rand = Math.random();
if (rand > 0.5) {
resolve(10);
console.log("promise1 se splnil");
} else {
reject();
console.log("promise1 se nesplnil);
}
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 20);
});
let promise3 = new Promise((resolve, reject) => {
resolve(30);
});
Promise.any([promise1, promise2, promise3])
.then((hodnota) => {
console.log("hodnota prvního Promisu, který byl splněný: " + hodnota);
})
.catch((err) => {
console.log(err);
});
Promise.allSettled
Tato metoda bere jako parametr Iterable objekt (třeba pole) s Promisy a vrací Promise, který se splní až se všechny předané Promisy splní nebo nesplní. Tento Promise bude mít jako hodnotu pole objektů, které obsahují status promisu a hodnotu promisu nebo důvod proč se Promise nesplnil.
let rand = Math.random();
if (rand > 0.5) {
resolve(10);
console.log("promise1 se splnil");
} else {
reject("vygenerované číslo je menší nebo se rovná 0.5");
console.log("promise1 se nesplnil");
}
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 20);
});
let promise3 = new Promise((resolve, reject) => {
resolve(30);
});
Promise.allSettled([promise1, promise2, promise3])
.then((vysledky) => {
console.log("informace o jednotlivých Promisech:");
for (let vysledek of vysledky) {
console.log(vysledek);
}
});
Metoda Promise.allSettled na MDN
Promise.race
Tato metoda bere jako parametr Iterable objekt (třeba pole) s Promisy a vrací Promise, který se jako první splní nebo nesplní.
setTimeout(resolve, 1000, "první Promise");
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "druhý Promise");
});
Promise.race([promise1, promise2])
.then((hodnotaPromisu) => {
console.log(hodnotaPromisu);
});
Promise.resolve
Tato metoda vrací splněný Promise. Jako parametr můžeme předat hodnotu, kterou bude Promise mít.
.then((hodnota) => {
console.log(hodnota);
});
Promise.reject
Tato metoda vrací nesplněný Promise. Jako parametr můžeme předat hodnotu, kterou bude Promise mít.
.catch((hodnota) => {
console.log(hodnota);
});