Implementar promesas: resolve / reject

  • Por
Artículo práctico para explicar un código de implementación de promesas ECMAScript 2015, y las funciones resolve y reject.

En una entrega anterior del Manual de ES6 estuvimos hablando de las promesas y explicamos cómo nos pueden ayudar a organizar nuestro código. Vimos que en el lenguaje Javascript es fácil caer en lo que se conoce como "código spaguetti" y que las promesas nos ofrecen una vía excelente para organizar nuestro código evitando la pirámide de callbacks. Puedes consultar la Introducción a las promesas de ES6 para más información.

En esta ocasión vamos a ampliar la información en lo referente a la implementación de una función que devuelve una promesa, tratando tanto los casos positivos (éxito) como los casos negativos (fracaso). Además ahondaremos en otra información importante de lenguaje Javascript y la nueva API de acceso a recursos Ajax: fetch.

Nota: Si no conoces fetch también te recomendamos estudiarlo antes de abordar este artículo, en el artículo de introducción a fetch.

Funciones resolve y reject

Cuando implementamos una función que devuelve una promesa tenemos a nuestra disposición dos funciones que permiten devolver el control al código que invocó la promesa. Esas funciones devuelven datos cuando la promesa se ejecutó normalmente y produjo los resultados esperados y cuando la promesa produjo un error o no pudo alcanzar los resultados deseados.

Nota: En el artículo de Introducción a las promesas de ES6 ya vimos cómo usar el resolve para devolver el control en el caso positivo, pero no hemos visto cómo usar el reject para informar de un caso negativo.

El código de una función que devuelve una promesa tiene una forma inicial como esta:

function devuelvePromesa() {
  return new Promise( (resolve, reject) => {
        //realizamos nuestra operativa…
  })
}

Como puedes ver, este código devuelve una nueva promesa. Para crear la promesa usamos "new Promise". El constructor de la promesa lo alimentamos con una función que recibe dos parámetros: resolve y reject, que son a su vez funciones que podremos usar para devolver valores tanto en el caso positivo como en el caso negativo.

Nota: Obviamente esos parámetros los puedes llamar como te apetezca, ya que son solo parámetros, pero los nombres indicados ya nos indican para qué sirve cada uno.

Ahora veamos nuestra operativa, que podría tener una forma como esta:

function devuelvePromesa() {
  return new Promise( (resolve, reject) => {
    setTimeout(() => {
      let todoCorrecto = true;
      if (todoCorrecto) {
        resolve('Todo ha ido bien');
      } else {
        reject('Algo ha fallado)
      }
    }, 2000)
  })
}

En nuestro código realizaremos cualquier tipo de proceso, generalmente asíncrono, por lo que tendrá un tiempo de ejecución durante el cual se devolverá el control por medio de una función callback. En la función callback podremos saber si aquel proceso se produjo de manera correcta o no. Si fue correcto usaremos la función resolve(), mandando de vuelta como parámetro el valor que se haya conseguido como resultado. Si algo falló usaremos la función reject(), enviando el motivo del error.

Podremos usar esa función que devuelve la promesa con un código como este:

devuelvePromesa()
  .then( respuesta => console.log(respuesta) )
  .catch( error => console.log(error) )

Como ya supondrás, then() recibe la respuesta indicada en el resolve() y catch() el error indicado en el reject.

Cómo implementar una conexión Ajax con fetch que devuelve un texto

En el artículo de fetch vimos que es un nuevo modelo de trabajo con Ajax, pero usando promesas. Vimos que un fetch() te devuelve una respuesta del servidor, con datos sobre la solicitud HTTP, pero si lo que queríamos es acceder al texto de la respuesta, necesitábamos encadenar una segunda promesa, llamando al método text() sobre la respuesta. Esa segunda promesa nos complicó un poco el código de algo tan sencillo como es: dada una URL recibir el texto que hay en el recurso.

A continuación vamos a poner un código de alternativa, que realiza ambas promesas y directamente nos devuelve el texto de respuesta. Como es un código asíncrono, que tardará un poco en ejecutarse y después de ello debe devolver el control al script original, lo implementaremos por medio de una promesa.

function obtenerTexto(url) {
  return new Promise( (resolve, reject) => {
    fetch(url)
      .then(response => {
        if(response.ok) {
          return response.text();
        }
        reject('No se ha podido acceder a ese recurso. Status: ' + response.status);
      })
      .then( texto => resolve(texto) )
      .catch (err => reject(err) );
  });
}

Este código usa el modelo de encadenado de promesas, ejecutando operaciones asíncronas, una cuando termina la anterior.

Comenzamos haciendo el fetch() a una URL que recibimos por parámetro. Ese fetch devuelve una promesa, que cuando se ejecuta correctamente nos entrega la respuesta del servidor.

Si la respuesta estuvo bien (response.ok es true) entonces devuelve una nueva promesa entregada por la ejecución de la función response.text(). Si la respuesta no estuvo bien, entonces rechazamos la promesa con reject(), indicando el motivo por el que estamos rechazando con un error.

A su vez el código de response.text(), que devolvía otra promesa, puede dar un caso de éxito o uno de error. El caso de éxito lo tratamos con el segundo then(), en el que aceptamos la promesa con el resolve.

Tanto para el caso de error de fetch() como para un caso de error en el método text(), como para cualquier otro error detectado (incluso un código mal escrito) realizamos el correspondiente catch(), rechazando nuestra promesa original.

Nota: Observa que un solo catch() te sirve para detectar cualquier tipo de error, tanto en la primera promesa (fetch) como en la segunda (text).

Este código lo puedes usar de la siguiente manera. Verás que tenemos un único método que nos devuelve directamente el texto.

obtenerTexto('test.txt')
  .then( texto => console.log(texto) )
  .catch( err => console.log('ERROR', err) )

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir