Wyjątki w łańcuchach Promise w JavaScript

Zakładając, że z jakichś powodów nasz Promise zakończy się błędem:

javascriptlet promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    reject('error');
  }, 3000);
});

W tym przypadku wykonanie kodu natychmiast przejdzie do tego then, w którym znajduje się funkcja obsługi błędu, albo do pierwszego catch, w zależności od tego, co zostanie napotkane wcześniej.

Oto przykład pierwszej sytuacji:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  },
  function(error) {
    // wykonanie natychmiast przejdzie tutaj
  }
).then(
  function(result) {
    console.log(result);
  }
);

Oto przykład drugiej sytuacji:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // wykonanie natychmiast przejdzie tutaj
  }
).then(
  function(result) {
    console.log(result);
  }
);

Funkcja obsługi ma dwie możliwości działania: jeśli poradziła sobie z sytuacją wyjątkową, może zwrócić wynik przez return i wykonanie będzie kontynuowane dalej wzdłuż łańcucha. Jeśli jednak nie poradziła sobie z błędem, może albo nic nie zwrócić, albo rzucić wyjątek przez throw. W tym przypadku wykonanie przejdzie do następnego przechwytywacza błędów (w then lub catch - cokolwiek napotkane wcześniej).

Z reguły wszystkie błędy łańcucha są przechwytywane w jednym miejscu: na końcu łańcucha umieszczany jest catch:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // trafimy tutaj w przypadku błędu
  }
);

Przy tym wyjątek może powstać w samym Promise, albo być rzucony przez throw w dowolnym ogniwie łańcucha:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    if (wszystkoDobrze) {
      return result + '2';
    } else {
      throw new Error('błąd'); // przechodzimy do najbliższego przechwytywacza
    }
  }
)
.then(
  function(result) {
    return result + '3';
  }
).catch(
  function(error) {
    // najbliższy przechwytywacz
  }
);

Pamiętaj, że catch jest potrzebny właśnie do diagnozowania błędu: czy jest on rozwiązywalny, czy nie. Jeśli błąd jest rozwiązywalny, to catch powinien przekazać jego rozwiązanie następnemu za sobą then. A jeśli nie jest rozwiązywalny (lub dany catch po prostu nie wie, jak go rozwiązać), to powinniśmy albo nic nie zwrócić, albo rzucić wyjątek:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    if (bladRozwiazywalny) {
      return 'dane'; // wysyłamy do następnego then
    } else {
      // nic nie zwracamy lub rzucamy wyjątek
    }
  }
).then(
  function(result) {
    // tutaj rozwiązujemy błąd
  }
);