Asynchronní kód v JavaScriptu pomocí Promise
Jen průřez používání Promise
v JS.
Promise
v JS slouží k čitelnějšímu zápisu datového toku1 asynchronního kódu. [mdn_using_promises]
Definujeme-li si pomocnou funkci delayMessage
:
1
2
3
4
5
6
const delayMessage=
(delay= 500, fail= false)=>
(message= "Default message")=>
new Promise(function(resolve,reject){
setTimeout(fail ? reject : resolve, delay, message);
});
Pomocná funkce delayMessage
pro následující ukázky
…pak následující jednoduché ukázky vrátí:
1
2
3
4
5
6
7
8
9
10
11
delayMessage()()
.catch(console.error)//chyba nenastala, přeskočí se
.then(console.log);//= "Default message"
delayMessage(500, true)()
.then(console.log)//nastala chyba, přeskočí se
.catch(console.error);//= "Default message"
delayMessage(500, true)("zpráva")
.catch(delayMessage())//nastala chyba + zpráva se předává dál
.then(console.log);//= "zpráva"
Průřez chováním Promise
Tedy pokud „v Promise
” („v .then
/.catch
”) nastane chyba, zavolá se nejbližší další .catch
– pokud ne, pak analogicky nejbližší další .then
. Doporučuji na konec řetízku dávat automaticky alespoň .catch(console.error)
aby nám neutekli chyby v asynchronním kódu!
Statickou hodnotu či vyjímku lze vyvolat pomocí metod Promise.resolve() a Promise.reject().
Skládání
Vlastně všechny statické metody v Promise
(např. Promise.all
) zpracovávají pole argumentů paralerně:
1
2
3
4
5
6
7
Promise.all([
delayMessage()("První zpráva"),
delayMessage(550)("Druhá zpráva")
])
.then(console.log) //= Array [ "První zpráva", "Druhá zpráva" ]
.catch(console.error);
//teoreticky zabere 550ms
Ukázka paralerního zpracování více Promise
pomocí Promise.all
… dále existují:
-
Promise.any(): První úspěšný
Promise
-
Promise.race(): První (ne)úspěšný
Promise
-
Promise.allSettled(): Narozdíl od
Promise.all
nikdy nespadne (vždy volá.then
) s polem výsledků jednotlivýchPromise
. Viz také [promise_all_vs_allsettled]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise.allSettled([
Promise.resolve(33),
new Promise(resolve => setTimeout(() => resolve(66), 0)),
99,
Promise.reject(new Error("an error"))
])
.then(values => console.log(values));
// [
// {status: "fulfilled", value: 33},
// {status: "fulfilled", value: 66},
// {status: "fulfilled", value: 99},
// {status: "rejected", reason: Error: an error}
// ]
Ukázka použití Promise.allSettled
Pro sekvenční volání si musíme dodefinovat např.2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const apply_= (promise, then_)=> promise.then(then_);
const sequential_=
(...promiseFuncs)=>
input=>
promiseFuncs.reduce(apply_, Promise.resolve(input));
sequential_(
delayMessage(),
msg=> Promise.resolve(msg+"\nPotvrzení „sekvenčnosti”"),
delayMessage(550)
)("Počáteční zpráva")
.then(console.log) //=Počáteční zpráva \nPotvrzení „sekvenčnosti”
.catch(console.error);
//teoreticky (500+550)ms
Pomocná funkce pro sekvenční volání Promise
Vhodné používání
Všimněte si, že implementace sequential_
vlastně „dělá Promise.resolve(input).then(func1).then(func2).then(func3);
”.
Tedy neděláme něco jako:
1
2
3
4
5
6
7
8
9
10
Promise.resolve(input)
.then(function(input){
func1(input)
.then(function(input){
func2(input)
.then(function(input){
func3(input);
});
});
})
Typicky nechtěné/špatné použití Promise
(pseudo kód inspirovaný sequential_
)
…toto je evidentě mnohem více nepřehledná implementace této funkcionality. Přesto může nastat, že budeme chtít asynchronní funkci obalit takovouto funkcí abychom například zachytili specifickou chybu. Avšak i v takovém případě bychom se měli snažit se „vrátit” do hlavního řetízku:
1
2
3
4
5
6
7
8
delayMessage()()
.then(function(input){
return delayMessage(500, true)(input).catch(input=> "Chyba zachycena");// zde vznikla chyba, ale nechceme aby probublala do hlavního řetízku
})
.then(delayMessage())
.then(delayMessage(500, true)) // chyba v hlavním řetízku
.then(console.log)
.catch(console.error);//= Chyba zachycena
Příklad chtěného zanořování v Promise
Pro správné pochopení/používání doporučuji projít oficiální dokumentaci [mdn_using_promises] a [mdn_using_microtasks].
Reference
- [mdn_using_promises] Using promises
- [mdn_using_microtasks] Using microtasks
- [promise_all_vs_allsettled] Promise.all vs Promise.allSettled in JavaScript
- [rauschma_concurrent] Using sequential-concurrent.js
- [maly_es_promises] maly / es-promises | Github
- [promise_utils] sindresorhus/promise-fun: Promise packages, patterns, chat, and tutorials
- [promise_generator] async/await with ES6 Generators & Promises – Spíše ukázka konceptu
-
Podtrzitko na konci názvu funkce používám jako úzus pro mé funkce vracející
Promise
. ↩