Excepciones en las cadenas de promesas en JavaScript

Supongamos que por alguna razón nuestra promesa se completa con un error:

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

En este caso, la ejecución del código pasará inmediatamente a aquel then que tenga una función manejadora de errores, o al primer catch, lo que se encuentre primero.

He aquí un ejemplo de la primera situación:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  },
  function(error) {
    // la ejecución pasará inmediatamente aquí
  }
).then(
  function(result) {
    console.log(result);
  }
);

He aquí un ejemplo de la segunda situación:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // la ejecución pasará inmediatamente aquí
  }
).then(
  function(result) {
    console.log(result);
  }
);

La función manejadora tiene dos opciones de acción: si ha manejado la situación excepcional, puede devolver un resultado mediante return y la ejecución continuará más adelante en la cadena. Si no ha podido manejar el error, puede o no devolver nada, o lanzar una excepción mediante throw. En este caso, la ejecución pasará al siguiente manejador de errores (en then o catch - lo que se encuentre primero).

Por regla general, todos los errores de la cadena se interceptan en un solo lugar: al final de la cadena se coloca un catch:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    // llegaremos aquí en caso de error
  }
);

En este caso, la excepción puede ocurrir en la propia promesa, o ser lanzada mediante throw en cualquier eslabón de la cadena:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    if (todoEstáBien) {
      return result + '2';
    } else {
      throw new Error('error'); // pasamos al interceptador más cercano
    }
  }
)
.then(
  function(result) {
    return result + '3';
  }
).catch(
  function(error) {
    // interceptador más cercano
  }
);

Tenga en cuenta que catch es necesario precisamente para diagnosticar el error: si es solucionable o no. Si el error es solucionable, entonces catch debe pasar su solución al then que le sigue. Y si no es solucionable (o este catch simplemente no sabe cómo resolverlo), entonces debemos o no devolver nada o lanzar una excepción:

javascriptpromise.then(
  function(result) {
    return result + '1';
  }
).then(
  function(result) {
    return result + '2';
  }
).catch(
  function(error) {
    if (errorSolucionable) {
      return 'datos'; // enviamos al siguiente then
    } else {
      // no devolvemos nada o lanzamos una excepción
    }
  }
).then(
  function(result) {
    // aquí resolvemos el error
  }
);