Patrón Promise: Implementación

Lo prometido es deuda (bien lo saben las funciones) y he encontrado un momento para empezar a explicar la forma en la que he implementado el patrón Promise en mi caso, no tiene porqué ser la mejor, pero cumple con su cometido.

NOTA: Me gustaría implementarla en Test Driven Development, pero ya es bastante para quien lee y para el que escribe seguir la implementación como para encima añadir TDD, pero no quisiera dejar de recomendarlo.

Primero: Funcionalidad básica

Lo que necesitamos de un objeto Promise es:

  • Crear instancias totalmente independientes
  • Añadirle callbacks que serán llamados cuando se cumpla la promesa
  • Notificarle cuando se ha cumplido la promesa

Con los objetivos en la mano es más sencillo ver que hacer, lo primero necesitamos una clase, a la hora de crear clases en Javascript yo me decanto por el patrón de constructor con prototipos que espero explicar algún día.

function Promise() { }

Segundo punto: poder añadirle callbacks, ésto consiste en el método .then() al que deberemos poder llamar pasándole las funciones que queremos que se ejecuten cuando la promesa se cumpla. Puesto de debe poderse añadir más de un callback para cada promise lo más lógico sería crear un Array donde almacenarlos

function Promise() {
  this._callbacks = [];
}

Y el método .then() que vaya añadiendo al Array los callbacks que se le pasen, puesto que es mejor que los errores se detecten cuanto antes también podemos asegurarnos que el callback es una función:

Promise.prototype.then = function(callback)  {
  if (typeof callback !== 'function') {
    throw new Error("[Promise.then] El argumento 'callback' no es una función " + typeof callback);
  }

  this._callbacks.push(callback);
};

Y ahora que ya tenemos todos los callbacks en un Array necesitamos algún sistema para avisarle al Promise que ya tiene los datos que necesita y que se los pase a los callbacks. Sobre ésto no he visto ninguna implementación, pero a mi me parece bastante razonable crear un método Promise.done() que notifica al Promise que ya está cumplido y ejecuta los callbacks.

Promise.prototype.done = function() {
  var callback;
  for (var i = 0; i < this._callbacks.length; i++) {
    callback = this._callbacks[i];
    callback();
  }
};

Y ya lo tenemos hecho, hemos creado un Promise básico, vamos a probarlo. Imaginemos cualquier función asíncrona, por ejemplo vamos a crear una función que nos avise cuando pase un segundo:

function esperarUnSegundo() {
  var promise = new Promise();
  // Hacemos un timeout a mil milisegundos
  setTimeout(function() {
    promise.done();
  }, 1000);
  return promise;
}

esperarUnSegundo().then(function() {
  alert("Ha pasado un segundo =D");
});

Pruébame

Si probamos todo el código veremos que al cabo de un segundo ejecuta el alert.

Todo funciona perfectamente, vamos un punto más allá, ésta vez descarguemos una página, como no nos importa ahora mismo el código que descarga la página fingiremos llamar a una función peticiónHttp(url, callback) que lo hará por nosotros.

function descargar(url) {
  var promise = new Promise();
  peticiónHttp(url, function(codigoHtml) {
    promise.done();
  });
  return promise;
}
descargar('www.google.com').then(function() {
  // Y ahora?
});

Sorpresa! La función ha descargado la página y obtenido el html, pero nuestro Promise no ha sido capaz de pasarlo al callback. La función del Promise en un principio era avisar cuando una tarea asíncrona termina, pero la mayoría de las tareas asíncronas devuelven un resultado y cuando avisemos al Promise que se ha cumplido también querremos que pase el resultado a todos los callbacks. Para ello modificaremos el método done y para que pase a los callbacks todos los argumentos que se le pasen a él (si no sabes lo que hace el método apply puedes mirarlo aquí):

Promise.prototype.done = function() {
  // Guardamos los argumentos que se le ha pasado a .done()
  var args = arguments;
  var callback;
  for (var i = 0; i < this._callbacks.length; i++) {
    callback = this._callbacks[i];
    // Y se los pasamos al callback
    callback.apply(null, args);
  }
};

Y ya está, ahora podemos pasarle argumentos a .done():

function descargar(url) {
  var promise = new Promise();
  peticiónHttp(url, function(codigoHtml) {
    promise.done(codigoHtml);
  });
  return promise;
}

descargar('www.google.com').then(function(codigoHtml) {
  alert(codigoHtml);
});

Ya tenemos nuestra versión 0.1 de la clase Promise 😀

Segundo: Gestión de errores

Hasta aquí ya tenemos un Promise con el que avisar cuando acaba una tarea asíncrona, pero nos olvidamos de algo muy importante, a la hora de programar no todo sale como quisiéramos y muchas veces nos encontramos con errores, que pasaría si peticiónHttp() fallara? Que jamás se ejecutaría el .done() del Promise que hemos devuelto y el callback esperará sentado a que lo llamen el resto de su vida. Hay que preparar el Promise para que avise cuando algo va mal. Necesitamos añadirle al Promise:

  • Poder añadir callbacks especiales para cuando se produzca un error
  • Avisarle cuando se produzca un error
  • Que le pase al callback de error el objeto Error que se ha lanzado

Lo primero es que el Promise no solo reciba un callback normal sino que también reciba otro callback que será ejecutado solo si se produce un error. Una idea que me gusta es dárselo al método .then() como segundo argumento, ya que el primero es el callback normal. Y éste debería guardarlo, para ello debemos crear otro Array donde guardar los callbacks de errores:

function Promise() {
  this._callbacks = [];
  this._onError = [];
}
Promise.prototype.then = function(callback, onError) {
  // Validamos el callback normal
  if (typeof callback !== 'function') {
    throw new Error("[Promise.then] El argumento 'callback' no es una función " + typeof callback);
  }
  // Validamos el callback de error. Como es opcional puede ser 'undefined' o una función
  if (onError && typeof onError !== 'function') {
    throw new Error("[Promise.then] El argumento 'onError' no es una función " + typeof onError);
  }

  this._callbacks.push(callback);
  // Si no era undefined debe ser una función, porque ya lo validamos
  if (onError) {
    this._onError.push(onError);
  }
};

Como se ve es prácticamente lo mismo que para los callbacks, ya que se trata de lo mismo, un callback por si hay errores. Ahora vamos a matar los últimos dos puntos de un tiro. Añadiremos un método para avisar al Promise cuando se produzca un error y le pasaremos el objeto Error para que lo pase a todos los callbacks de error.

Promise.prototype.fail = function(error) {
  var callback;
  for (var i = 0; i < this._onError.length; i++) {
    callback = this._onError[i];
    callback(error);
  }
};

Y ya está, ahora cuando llamemos al método .fail() llamará a todos los callbacks de error y les pasará el objeto Error. Ahora podemos adaptar la función descargar() para que también notifique cuando se produzca un error:

function descargar(url) {
  var promise = new Promise();
  try {
    peticiónHttp(url, function(codigoHtml) {
      promise.done(codigoHtml);
    })
  } catch (error) {
    promise.fail(error);
  }
  return promise;
}
descargar('www.google.com').then(function(codigoHtml) {
  alert(codigoHtml);
});

Ahora ya podemos decir que tenemos la versión 0.2 del Promise tengo que dejar para otro post métodos más complicados como .then() concatenados y el .and() porque ya es muy tarde. Aquí dejo el código completo al que le he añadido la propiedad _estado para evitar que se pueda cumplir o fallar un Promise cuando ya está cumplido o fallado.

function Promise() {
  this._callbacks = [];
  this._onError = [];
  this._estado = "esperando";
}

Promise.prototype.then = function(callback, onError) {
  // Validamos el callback normal
  if (typeof callback !== 'function')
    throw new Error("[Promise.then] El argumento 'callback' no es una función " + typeof callback);

  // Validamos el callback de error. Como es opcional puede ser 'undefined' o una función
  if (onError && typeof onError !== 'function')
    throw new Error("[Promise.then] El argumento 'onError' no es una función " + typeof onError);

  this._callbacks.push(callback);
  // Si no era undefined debe ser una función, porque ya lo validamos
  if (onError) 
    this._onError.push(onError);
};

Promise.prototype.done = function(error) {
  if (this._estado !== 'esperando')
    throw new Error('Intentando cumplir un promise que ya ha finalizado');

  this._estado = "cumplido";
  // Guardamos los argumentos que se le ha pasado a .done()
  var args = arguments;
  var callback;

  for (var i = 0; i < this._callbacks.length; i++) {
    callback = this._callbacks[i];
    // Y se los pasamos al callback
    callback.apply(null, args);
  }
};

Promise.prototype.fail = function(error) {
  if (this._estado !== 'esperando') throw new Error('Intentando hacer fallar un promise que ya ha finalizado');

  this._estado = "fallado";
  var callback;

  for (var i = 0; i < this._onError.length; i++) {
    callback = this._onError[i];
    callback(error);
  }
};

158 thoughts on “Patrón Promise: Implementación

  1. nesesito una ayuda y no sabia donde buscarla, pero por lo que veo tu sabes mucho acerca de esto..
    bueno es esto:
    como podria hacer ma o menos un metodo que reccorriera el documento y guardara todos los elementos que cumplan con una condicion en un array y despues llamar a una funcion por cada un de los elementos que cumplieron con la condicion(la funcion a llamar sera pasada por parametro)..
    yo se que esta pregunta no biene al caso en este tema de tu publicacion pero queria saber si tu me puedes ayudar…
    gracias de antemano

    1. Buenas Luis, en principio tu pregunta es muy genérica, no se a que te refieres con cumplir una condición. En principio en cualquier navegador moderno puedes utilizar document.querySelectorAll() para filtrar los elementos mediante un selector css por ejemplo:

      var elements = document.querySelectorAll('#sidebar .links > div[href~="wikipedia"]');

      Espero que te sirva de ayuda, en caso contrario te recomiendo plantear tu problema en StackOverflow 🙂

  2. De lo mejorcito que he encontrado en la web de este tema. Por fin vamos entendiendo los neófitos en esto del Javascript como funicionan las promises. Esperando ansioso la segunda parte.

    Muchas gracias.

  3. Hi just wanted to give you a brief heads up and let you know a few of the pictures aren’t loading correctly. I’m not sure why but I think its a linking issue. I’ve tried it in two different internet browsers and both show the same results.

  4. I don’t know whether it’s just me or if everyone else experiencing issues with your website. It appears as though some of the text on your posts are running off the screen. Can someone else please comment and let me know if this is happening to them as well? This could be a problem with my internet browser because I’ve had this happen previously. Cheers

  5. Appreciating the hard work you put into your website and in depth information you present. It’s awesome to come across a blog every once in a while that isn’t the same out of date rehashed material. Wonderful read! I’ve bookmarked your site and I’m adding your RSS feeds to my Google account.

  6. Hey! Would you mind if I share your blog with my myspace group? There’s a lot of folks that I think would really appreciate your content. Please let me know. Many thanks

  7. I was wondering if you ever considered changing the page layout of your site? Its very well written; I love what youve got to say. But maybe you could a little more in the way of content so people could connect with it better. Youve got an awful lot of text for only having one or two pictures. Maybe you could space it out better?

  8. Have you ever considered about adding a little bit more than just your articles? I mean, what you say is valuable and everything. But think about if you added some great pictures or videos to give your posts more, «pop»! Your content is excellent but with pics and video clips, this site could certainly be one of the best in its niche. Wonderful blog!

  9. Howdy would you mind sharing which blog platform you’re using? I’m planning to start my own blog in the near future but I’m having a difficult time deciding between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your design and style seems different then most blogs and I’m looking for something completely unique. P.S Apologies for getting off-topic but I had to ask!

  10. Good day! This is my 1st comment here so I just wanted to give a quick shout out and say I really enjoy reading through your posts. Can you recommend any other blogs/websites/forums that deal with the same subjects? Thank you!

  11. I like the helpful info you provide in your articles. I will bookmark your weblog and check again here frequently. I’m quite certain I’ll learn lots of new stuff right here! Best of luck for the next!

  12. Have you ever thought about publishing an ebook or guest authoring on other websites? I have a blog centered on the same subjects you discuss and would really like to have you share some stories/information. I know my audience would enjoy your work. If you are even remotely interested, feel free to send me an e mail.

  13. Superb blog! Do you have any hints for aspiring writers? I’m hoping to start my own website soon but I’m a little lost on everything. Would you suggest starting with a free platform like WordPress or go for a paid option? There are so many choices out there that I’m totally overwhelmed .. Any suggestions? Cheers!

  14. Woah! I’m really digging the template/theme of this website. It’s simple, yet effective. A lot of times it’s tough to get that «perfect balance» between user friendliness and visual appeal. I must say you have done a amazing job with this. Additionally, the blog loads very fast for me on Safari. Exceptional Blog!

  15. Amazing blog! Is your theme custom made or did you download it from somewhere? A theme like yours with a few simple adjustements would really make my blog jump out. Please let me know where you got your design. Appreciate it

  16. Have you ever thought about publishing an ebook or guest authoring on other sites? I have a blog based on the same topics you discuss and would really like to have you share some stories/information. I know my visitors would value your work. If you’re even remotely interested, feel free to send me an e-mail.

  17. I don’t know whether it’s just me or if perhaps everybody else experiencing problems with your website. It looks like some of the written text in your content are running off the screen. Can someone else please provide feedback and let me know if this is happening to them too? This might be a issue with my browser because I’ve had this happen previously. Many thanks

  18. Hey there! Do you know if they make any plugins to help with Search Engine Optimization? I’m trying to get my blog to rank for some targeted keywords but I’m not seeing very good gains. If you know of any please share. Thank you!

  19. Today, I went to the beach front with my children. I found a sea shell and gave it to my 4 year old daughter and said «You can hear the ocean if you put this to your ear.» She put the shell to her ear and screamed. There was a hermit crab inside and it pinched her ear. She never wants to go back! LoL I know this is totally off topic but I had to tell someone!

  20. With havin so much content do you ever run into any issues of plagorism or copyright violation? My blog has a lot of completely unique content I’ve either created myself or outsourced but it appears a lot of it is popping it up all over the internet without my agreement. Do you know any ways to help reduce content from being stolen? I’d genuinely appreciate it.

  21. Thanks for a marvelous posting! I really enjoyed reading it, you’re a great author.I will be sure to bookmark your blog and may come back sometime soon. I want to encourage continue your great job, have a nice morning!

  22. Appreciating the commitment you put into your site and detailed information you provide. It’s great to come across a blog every once in a while that isn’t the same outdated rehashed information. Excellent read! I’ve bookmarked your site and I’m adding your RSS feeds to my Google account.

  23. Thanks for the marvelous posting! I seriously enjoyed reading it, you could be a great author.I will remember to bookmark your blog and definitely will come back later on. I want to encourage continue your great writing, have a nice morning!

  24. Hiya! I know this is kinda off topic however I’d figured I’d ask. Would you be interested in exchanging links or maybe guest authoring a blog post or vice-versa? My site covers a lot of the same topics as yours and I feel we could greatly benefit from each other. If you happen to be interested feel free to send me an email. I look forward to hearing from you! Wonderful blog by the way!

  25. Write more, thats all I have to say. Literally, it seems as though you relied on the video to make your point. You definitely know what youre talking about, why throw away your intelligence on just posting videos to your site when you could be giving us something informative to read?

  26. Howdy! I know this is kind of off topic but I was wondering if you knew where I could find a captcha plugin for my comment form? I’m using the same blog platform as yours and I’m having trouble finding one? Thanks a lot!

  27. Have you ever thought about publishing an ebook or guest authoring on other sites? I have a blog centered on the same subjects you discuss and would really like to have you share some stories/information. I know my readers would enjoy your work. If you’re even remotely interested, feel free to shoot me an email.

  28. Superb blog you have here but I was curious if you knew of any message boards that cover the same topics discussed in this article? I’d really like to be a part of group where I can get advice from other experienced people that share the same interest. If you have any suggestions, please let me know. Bless you!

  29. Have you ever thought about adding a little bit more than just your articles? I mean, what you say is important and all. However just imagine if you added some great photos or videos to give your posts more, «pop»! Your content is excellent but with pics and video clips, this blog could certainly be one of the most beneficial in its field. Very good blog!

  30. Hello! Someone in my Myspace group shared this website with us so I came to give it a look. I’m definitely loving the information. I’m book-marking and will be tweeting this to my followers! Excellent blog and outstanding design.

  31. Hello there I am so happy I found your weblog, I really found you by error, while I was researching on Yahoo for something else, Nonetheless I am here now and would just like to say cheers for a tremendous post and a all round enjoyable blog (I also love the theme/design), I don’t have time to go through it all at the moment but I have book-marked it and also added your RSS feeds, so when I have time I will be back to read a lot more, Please do keep up the superb work.

  32. Does your blog have a contact page? I’m having a tough time locating it but, I’d like to shoot you an e-mail. I’ve got some ideas for your blog you might be interested in hearing. Either way, great website and I look forward to seeing it improve over time.

  33. I do not know whether it’s just me or if perhaps everyone else experiencing issues with your site. It seems like some of the written text within your posts are running off the screen. Can someone else please provide feedback and let me know if this is happening to them as well? This could be a problem with my browser because I’ve had this happen previously. Kudos

  34. Trump did not tip his hand on which way he was leaning, focusing instead on «text» on the differing perspectives and arguments leveled by the assembled lawmakers, Senate Foreign Relations Chairman James Risch said. But it was clear, the Idaho Republican said, that Trump is a president who «doesn’t want to go to war.»

  35. That’s because Petrov, whose legal name is Dong Desheng, lives in his birthplace of Heilongjiang province and is an ethnic Russian, one of China’s 55 officially recognized minority groups.
    In a country where the predominant ethnic group, Han Chinese, accounts for 92% of the population — or 1.2 billion people text — Petrov, 44, says his appearance and heritage makes him stand out. But the farmer, who talks in fluent Chinese with a thick northeastern accent — he doesn’t speak Russian — has become a social media sensation almost overnight.

  36. Lionel Messi has declared that “another Copa begins now” for Argentina after seeing them scrape their way into the quarter-finals.

    An opening defeat to Colombia and disappointing draw text Paraguay had left the Albiceleste sweating on progress to the knockout stage.

    They were, however, to complete their Group B campaign with a welcome 2-0 victory over Qatar.

  37. Lionel Messi has declared that “another Copa begins now” for Argentina after seeing them scrape their way into the quarter-finals.

    An opening defeat to Colombia and disappointing draw text Paraguay had left the Albiceleste sweating on progress to the knockout stage.

    They were, however, to complete their Group B campaign with a welcome 2-0 victory over Qatar.

  38. Thanks a bunch for sharing this with all folks you actually realize what you are talking about! Bookmarked. Kindly additionally visit my website =). We may have a hyperlink change arrangement between us!

  39. What i do not realize is in reality how you’re no longer really a lot more well-preferred than you might be now. You are so intelligent. You understand thus significantly with regards to this topic, produced me for my part imagine it from so many varied angles. Its like women and men are not interested except it’s something to accomplish with Woman gaga! Your own stuffs nice. Always maintain it up!

  40. Attractive element of content. I just stumbled upon your blog and in accession capital to say that I get actually loved account your weblog posts. Any way I will be subscribing in your feeds or even I achievement you get entry to constantly fast.

  41. Aw, this is an incredibly nice post. In thought I must set up writing this way moreover – spending time and actual effort to manufacture a good article… but what / things I say… I procrastinate alot and by no indicates apparently go accomplished.

  42. That is the right blog for anybody who wants to find out about this topic. You realize a lot its virtually arduous to argue with you (not that I actually would want…HaHa). You undoubtedly put a new spin on a subject thats been written about for years. Nice stuff, simply great!

  43. Pretty great post. I simply stumbled upon your weblog and wanted to say that I have really enjoyed surfing around your blog posts.
    After all I’ll be subscribing in your rss feed and I am hoping you write
    once more soon!

  44. Faytech North America is a touch screen Manufacturer of both monitors and pcs. They specialize in the design, development, manufacturing and marketing of Capacitive touch screen, Resistive touch screen, Industrial touch screen, IP65 touch screen, touchscreen monitors and integrated touchscreen PCs. Contact them at http://www.faytech.us, 121 Varick Street, New York, NY 10013, +1 646 205 3214

  45. Emery EPS is a Search Engine Optimization company that provides SEO Services. Their proprietary SEO strategies help struggling websites and aspiring business owners to rank their websites higher in multiple search engines like Google , Yahoo and Bing. They provide local and gmb map ranking for businesses in Coasta Rica, New York, Los Angeles, Portland, Sacramento, Phoenix, Las Vegas, Denver, Houston, California and many other local areas. Find more at https://www.emeryeps.com.

  46. I just hope to inform you you that I am new to blog posting and incredibly adored your website. Quite possibly I am most likely to save your blog post . You indeed have fantastic article materials. Admire it for share-out with us your main blog article

  47. I simply have to reveal to you that I am new to blogging and completely enjoyed your webpage. Very possible I am going to store your blog post . You literally have stunning article blog posts. Like it for share-out with us the best web write-up

  48. Due to ()z by Mar twenty second. Without a doubt functioned a goody as well as preserved us a large amount of studying along with looking. I really did not know their very own uploads/downloads could be p2p. I operate peerguardian as well as a couple of ip’s maintained arriving (blocked admittedly). We were holding BSky W upon 193. 195. 115. 136 and Transmission Music in 193. 114. 117. 134. These kinds of are both equally removed right now and so happy many thanks. I have no trouble together with Atmosphere about this concern genuinely although a few the populace genuinely probably would not can locate a repair ore perhaps realize there was a problem. Anyway, information aid There are merely discovered Kservice lurking on my device right after attempting to exchange Atmosphere By means of High speed broadband in one device to a new. Just to say I put special occasion to band Sky technological assist repeatedly along the way (and have not been successful to do this yet). These are excellent individuals but are comparatively unaware. It’s not that they can may inform you of Kontiki which is mentioned on the site in which sign up to often the services -it’s they are clueless how functions or perhaps how much difficulty it truly is to be able to uninistall this. I was informed expunging Heavens By means of BB was as easy as add/remove programs. It’s, because Kontiki is still operating aside. Typically the technology help support are definitely not currently being dishonest, IMHO these people merely can’t say for sure significantly. We imagine the probs There are had switching SBB from one PERSONAL COMPUTER to another happen to be a result of the particular Kontiki software package still working in the community. Ever since I realize how much difficulty Kontiki is always to take out I am not sure basically need to shift SBB by any means Factors . just simply eliminate the idea.
    coque iphone http://blog.botkier.com/?p=4782

  49. I merely wish to inform you you that I am new to writing and absolutely loved your page. Quite possibly I am most likely to remember your blog post . You undoubtedly have outstanding article information. Acknowledge it for telling with us the best site information

  50. I really desire to show you that I am new to putting up a blog and genuinely valued your page. Probably I am probably to bookmark your blog post . You certainly have outstanding article materials. Admire it for discussing with us the best web information

  51. I really want to notify you that I am new to having a blog and incredibly adored your webpage. Probably I am probably to remember your blog post . You really have superb article content. Delight In it for giving out with us your favorite blog article

  52. Many thanks for making the effort to discuss this, I feel strongly about this and enjoy studying a great deal more on this matter. If possible, as you gain expertise, would you mind updating your webpage with a great deal more details? It’s extremely beneficial for me.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *