Исключения в цепочках промисов в JavaScript

Пусть по каким-то причинам наш промис завершится с ошибкой:

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

В этом случае выполнение кода сразу перейдет к тому then, в котором есть функция-обработчик ошибки, либо в первому catch, смотря что встретится раньше.

Вот пример первой ситуации:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  },
  function(error) {
    // выполнение сразу перейдет сюда
  }
).then(
  function(result) {
    console.log(result);
  }
);

Вот пример второй ситуации:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // выполнение сразу перейдет сюда
  }
).then(
  function(result) {
    console.log(result);
  }
);

Функция-обработчик имеет два варианта действий: если она справилась с исключительной ситуацией, то может вернуть результат через return и выполнение продолжится дальше по цепочке. Если же она не справилась с ошибкой, то может или ничего не возвращать, или выбросить исключение через throw. В этом случае выполнение перейдет к следующему перехватчику ошибки (в then или catch - что встретится раньше).

Как правило, все ошибки цепочки перехватываются в одном месте: в конце цепочки размещается catch:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // попадем сюда в случае ошибки
  }
);

При этом исключение может возникнуть в самом промисе, либо выброшено через throw в любом звене цепочки:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    if (всеХорошо) {
      return result + '2';
    } else {
      throw new Error('ошибка'); // переходим к ближайшему перехватчику
    }
  }
)
.then(
  function(result) {
    return result + '3';
  }
).catch(
  function(error) {
    // ближайший перехватчик
  }
);

Учтите, что catch нужен именно для диагностики ошибки: она решаема или нет. Если ошибка решаема, то catch должен передать ее решение следующему за собой then. А если не решаема (или данный catch просто не знает как ее решить), то мы должны или ничего не вернуть или бросить исключение:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    if (ошибкаРешаема) {
      return 'данные'; // отправляем на следующий then
    } else {
      // ничего не возвращаем или бросаем исключение
    }
  }
).then(
  function(result) {
    // тут решаем ошибку
  }
);