JS Promisy

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í.

// vytvoření Promisu
let pujduBehat = new Promise((resolvereject) => {
    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 mujPromise = new Promise((resolvereject) => {
    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 pujduBehat = new Promise((resolvereject) => {
    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.

function pockejChvili() {
    return new Promise((resolvereject) => {
        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 mujPromise = new Promise((resolvereject) => {
    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 then na MDN

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 mujPromise = new Promise((resolvereject) => {
    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 catch na MDN

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 mujPromise = new Promise((resolvereject) => {
    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");
});
>

Metoda finally na MDN

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ů.

let promise1 = new Promise((resolvereject) => {
    resolve(10);
});
let promise2 = new Promise((resolvereject) => {
    setTimeout(resolve50020)// za půl sekundy zavolá funkci resolve s argumentem 20
});
let promise3 = new Promise((resolvereject) => {
    let rand = Math.random();
    if (rand > 0.5) {
        resolve(30);
        console.log("promise3 se splnil");
    } else {
        reject();
        console.log("promise3 se nesplnil");
    }
});

Promise.all([promise1promise2promise3]) // 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);
});
>

Metoda Promise.all na MDN

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 promise1 = new Promise((resolvereject) => {
    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((resolvereject) => {
    setTimeout(resolve50020);
});
let promise3 = new Promise((resolvereject) => {
    resolve(30);
});

Promise.any([promise1promise2promise3])
.then((hodnota) => {
    console.log("hodnota prvního Promisu, který byl splněný: " + hodnota);
})
.catch((err) => {
    console.log(err);
});
>

Metoda Promise.any na MDN

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 promise1 = new Promise((resolvereject) => {
    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((resolvereject) => {
    setTimeout(resolve50020);
});
let promise3 = new Promise((resolvereject) => {
    resolve(30);
});

Promise.allSettled([promise1promise2promise3])
.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í.

let promise1 = new Promise((resolvereject) => {
    setTimeout(resolve1000"první Promise");
});
let promise2 = new Promise((resolvereject) => {
    setTimeout(resolve500"druhý Promise");
});

Promise.race([promise1promise2])
.then((hodnotaPromisu) => {
    console.log(hodnotaPromisu);
});
>

Metoda Promise.race na MDN

Promise.resolve

Tato metoda vrací splněný Promise. Jako parametr můžeme předat hodnotu, kterou bude Promise mít.

Promise.resolve("splněno")
.then((hodnota) => {
    console.log(hodnota);
});
>

Metoda Promise.resolve na MDN

Promise.reject

Tato metoda vrací nesplněný Promise. Jako parametr můžeme předat hodnotu, kterou bude Promise mít.

Promise.reject("nesplněno")
.catch((hodnota) => {
    console.log(hodnota);
});
>

Metoda Promise.reject na MDN