Asynchronní kód v JavaScriptu pomocí Promise

Jen průřez používání PromiseJS.


Obsah
  1. Skládání
  2. Vhodné používání
  3. Reference

PromiseJS 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()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í:

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][mdn_using_microtasks].

Reference

  1. [mdn_using_promises] Using promises
  2. [mdn_using_microtasks] Using microtasks
  3. [promise_all_vs_allsettled] Promise.all vs Promise.allSettled in JavaScript
  4. [rauschma_concurrent] Using sequential-concurrent.js
  5. [maly_es_promises] maly / es-promises | Github
  6. [promise_utils] sindresorhus/promise-fun: Promise packages, patterns, chat, and tutorials
  7. [promise_generator] async/await with ES6 Generators & Promises – Spíše ukázka konceptu
  1. Dataflow - Wikipedia 

  2. Podtrzitko na konci názvu funkce používám jako úzus pro mé funkce vracející Promise