Explicando Promises de JavaScript con un ejemplo simple

Hace unos días tuve la oportunidad de ver, una vez más, explicado el concepto de promesas y de objetos asíncronos, cuyo objetivo es, entre otros, evitar el llamado «callback-hell» que surge cuando llamamos a una función asíncrona en JavaScript.

En nuestro código frontend, es bastante habitual encontrarnos con funciones con este aspecto:

function myFunction(arguments,onSuccess,onError){
//Do things...
}

myFunction({val: 12}, function(){
    //Do things on success
}, function(){
    //Do things on error
});

El problema surge cuando encadenamos varias llamadas asíncronas, y ya no somos capaces de saber en qué función estamos ní cómo hemos llegado allí, además que el código se hace más difícil de leer.

Las promises o promesas, son objetos que nos permiten mejorar la legibilidad de nuestro código y evitar tener que pasar el contenido de las funciones directamente como argumentos a nuestra llamada.

En este artículo veremos una implementación muy simple de una promesa, para ver cómo funciona realmente, y comprobar que la base es un objeto simple con ciertas propiedades.

Nuestra clase Promise

Veamos por encima qué tenemos en nuestra clase Promise:

function Promise(){

    var self = this;
    var thenCallback = null;

    self.then = function(callback){
        thenCallback = callback;
    };

    self.complete = function(args){
        if (thenCallback && typeof thenCallback === 'function'){
            thenCallback(args);
        }
    };
}

Esta clase define dos funciones, una función then, en la que especificamos la función a ejecutar al terminar la acción, y una función complete, que nos permite ejecutar la función que hayamos definido, pasandole los argumentos necesarios.

Veamos un ejemplo de uso:

function myFunction(){

    var p = new Promise();
    setTimeout(p.complete, 1000)
    return p;
}

var promise = myFunction()
promise.then(function(){
    console.log('done');
});

En este caso, por una parte simulamos una llamada asíncrona con la función setTimeout, y por otra parte devolvemos la promesa. En el código que llama a la función, nos suscribimos al resultado de esa promesa, y podemos separar la ejecución de la función del tratamiento del resultado de la misma.

Gestión de errores y encadenamiento de promesas

Además de ejecutar funciones cuando todo ha ido bien, hemos de ser capaces de ejecutar acciones cuando se ha producido un error.

Para ello solamente tenemos que extender nuestra clase, agregando dos funciones adicionales, una se suscriba al error, que llamaremos error, y otra que ejecute la acción de error, que llamaremos fail.

self.error = function(callback){
        failCallback = callback;
};

self.fail = function(args){
    if (failCallback && typeof failCallback === 'function'){
        failCallback(args);
    }
}

Por otra parte, mediante un pequeño cambio podemos encadenar resultados:

self.error = function(callback){
        failCallback = callback;
        return self;
};

Este encadenamiento nos permite tener interfaces más fluidas, de tal manera que por una parte nos suscribimos al resultado de una función, y por otra parte al posible error:

promise.then(function(args){
    console.log('foo has happened ' + args);
}).error(function(args){
    console.log('error has happened');
});

Funciones «always»

Además de código que se ejecute cuando todo ha ido bien, o cuando todo ha ido mal, es posible que necesitemos código que se ejecute siempre, independientemente del resultado, lo que sería equivalente a la directiva «finally» de una función de C#.

Para ello solamente necesitamos agregar un nuevo callback a nuestra lista, que se ejecutará en caso de éxito o de fallo.

Conclusiones

Como vemos, las promesas no son «magia» que tienen algunas bibliotecas de JavaScript. Simplemente es otra sintaxis, otra manera de recuperar el control tras volver de una función asíncrona.

Si echamos un vistazo a lo que se propone para ECMAScript 6, lo que en la práctica será la próxima versión de JavaScript, veremos que las ideas son muy similares a lo que hemos visto en este artículo, aunque más extendido y detallado.

Tienes el código completo de la promesa en este gist

Enlaces adicionales

Diferentes tipos de «Promises»

Gracias a Antón Molleda (@molant) por los enlaces!

Autor: Roberto Luis Bisbé

Software Developer, Computer Engineer

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.