Starting from:
Don't promise what you can't deliver
You might assume that a promise in Javascript is based on this principle by ensuring that there will always be a ( expected? ) result.
Taking this code as an example:
var promesa = new Promise(
function(resolve, reject) {
alert('Hola mundo!');
}
);
promesa.then(
function(value) {
alert('Hola universo!');
}).then(
function(value) {
alert('Hola multiuniverso!');
});
A serial process is triggered until show alert('Hola multiuniverso!');
, which could also be done with nested Ajax Requests , which leads me to assume that there are also other advantages over a simple Ajax call.
The main questions would be (I know there are several and it is not so well seen within SOes, but with the explanation of what a promise is, several of them are resolved):
- What is a promise?
Promise
a plugin , a standard or a design pattern?- Is ajax a promise type?
- Can promises be synchronous and asynchronous calls?
As an introduction, I will tell you that promises go way beyond AJAX requests. They are not directly related, except for the concept of asynchrony they share.
What is a promise?
Mozilla gives us a fairly clear and concise concept:
A promise, as its name implies, is simply an object that may or may not return some value in the present and future timeline. I like to describe a promise as a kind of Karma:
This applies the same to promises, you execute asynchronous code and get the promise that you will get a response, which can be now or in the future.
Life cycle of a promise
The history of promises goes back to the Promises/A+ specification which details how any compiler implementation should implement them.
A Promise remembers the context in which it is executed, that is, it knows precisely at what point a value is to be resolved or an error is thrown. When a promise is executed, it has 2 states, an initial one and a final one:
Initially, a promise has the pending state, which state it will have until the promise has resolved a value using
resolve
or an error (reject
) has occurred. When a promise reaches one of these two states, it can no longer transition to another.Broadly speaking, Promises ES6 implements the Promises/A+ specification in much the same way. However, unlike the Promises/A+ specification, where there is no mention of a special method for catching errors, the ECMAScript implementation added a method
catch
that will resolve any errors that occurred during code execution.Is it a plugin, a standard or a design pattern?
It is not a design pattern
It would be like a kind of architecture . The concept of promises is linked to concurrent programming. Both Promises, Delay, Future (well known in Java) and Deferred, these act as a proxy for a result that we initially do not know.
It is not a plugin
Promise is a specification, not a plugin; although there are several plugins to use; the best known is
Q
.yes it is a standard
In fact, the Promises specification goes back further than ES2015. Promises is a specification known as Promses/A+ which was implemented in ECMAScript 2015/6, therefore this specification belongs to the ECMAScript standard which is currently 98% implemented in most modern browsers (Safari Tech Preview is the only browser to implement ES6 100%).
You can see the current proposals in the committee repository .
Is ajax a promise type?
It's not a promise type, it's a specification. AJAX, from the acronym Asynchronous JavaScript And XML , is a technique for making asynchronous requests; whose main effect is to obtain a response in an indeterminate time but without affecting the main process, which avoids a reload of the current document. The history of AJAX dates back to the late 1990's, during the development of Microsoft Outlook.
How AJAX works
You can see the spec
XMLHttpRequest
in the W3 living draft .As I said in the introduction, AJAX has nothing to do directly with Promises because they don't implement it, at least not
XMLHttpRequest
. On the other hand, jQuery implements its own deferred system which is used in$.ajax
or directly by$.deferred
. However, a new API for AJAX has recently come to light:fetch
it does use Promises natively.Can promises be synchronous and asynchronous calls?
No. A promise will always be asynchronous and there is no way to make it synchronous. What can be done is to make it look as if it were synchronous and this is thanks to the fact
Async/Await
that it has just reached stage 4 and will be included in this year's ECMAScript version. Using this new specification we can wait for the response while still being asynchronous, as if it were a standard synchronous function.Example (Chromium 55+ is required to run)
As you can see, at first glance it seems that we execute the promise synchronously/sequentially, but this is not the case and something magical happens behind the scenes. When we use
await
what we do is wait for the response of that promise. Technically, the execution -without blocking- of the code is being paused until the promise is resolved (fullfilled or rejected).Once the promise has been resolved, the execution of the code will continue. If you need to understand in detail how the VM and the EventLoop works, I highly recommend watching this video from the JSFOO conference last year.
Should I use Promises?
My answer is absolutely yes . Use them whenever you can, it improves semantically and it's a cognitive improvement too (if you've seen a callback hell it gives you a stroke), also, together with asynchronous functions (
async/await
) you can have a clean, tidy and above all asynchronous code; What Python achieved with asyncio!However, you should not discard callbacks because they are a fundamental piece in a functional language. Promises weren't here to replace callbacks , but as a complement to the language. In general terms, using promises provides us with greater control of the asynchronous code thanks to particularities that callbacks have and lack (such as chaining).
Some pros:
async/await
Sources:
Understanding promises and their importance
There is something fundamental to be able to understand the promises and the revolution that they imply. JavaScript is single threaded, meaning two portions of script cannot be executed at the same time, they have to be executed one after the other. In browsers, JavaScript shares a thread with a load of other things that differs from browser to browser. But JavaScript is generally in the same queue as painting, updating styles, and controlling user actions (such as highlighting text and interacting with form controls). Activity in one of these elements retards the others.
To avoid that, so far events and callbacks have been used.
For example:
Unfortunately, in the example above, it is possible that the events occur before we start to hear them. Therefore, we must solve this problem using the "complete" property of images:
This does not capture images that generated an error before we could hear them. Unfortunately, the DOM doesn't give us a way to do this. Also, in this example, we are only trying to load an image. The complexity increases further when we want to know when a set of images was loaded.
Events are great for things that can happen multiple times on the same object, because then you don't really want to know what happened before you attached the listener. But if it's asynchronous success/failure, ideally, you'd want something like this:
Promises do that, albeit with better nomenclature. If HTML image elements had a "ready" method that returned a promise, we could do the following:
Fundamentally, promises are a bit like event listeners, except for the following:
This is extremely useful for the success or failure of asynchronous processes because the exact moment of availability is less important than the reaction to the result.
So... what are promises?
They are an API that will help us to do things that were previously complicated or impossible due to what has been said above.
The image shows us the life cycle and the operation of a promise. As promised , I have translated the explanations of the image :)
A promise can be of these classes:
fulfilled
(fulfilled): The action related to the promise completes successfully.rejected
(rejected): The action related to the promise does not complete successfully.pending
(pending) - Not yet completed or rejected.settled
(Completed) – Completes or rejects.In the specifications , the term also appears
thenable
to describe a promise-like object because it has a methodthen
.Needless to say, promises have existed in the form of libraries for a long time. The following are some:
The promise revolution started through libraries like these and frameworks that use them as a primary means of manipulating asynchrony in their code. Since 2013 promises are available natively in modern browsers, which will be decisive in the future.
These libraries and JavaScript promises have in common a standardized behavior called Promises/A+. If you're using jQuery, you'll find something similar called Deferred. However, Deferred doesn't honor Promise/A+, so it's a bit different and less useful, so be careful. jQuery also has a Promise type, but that's just a subset of Deferred and doesn't work very well.
While the implementations of the promises adhere to standardized behavior, the general APIs are different. The JavaScript Promise APIs are similar to RSVP.js.
JavaScript promises started in the DOM as “Future”, were renamed to “Promise”, and eventually moved to JavaScript. It's great to have them in place of the DOM in JavaScript because they'll be available in non-browser JS contexts like Node.js.
Although they are a JavaScript feature, they are used by the DOM when needed. In fact, all new DOM APIs with asynchronous success or failure methods use promises.
Seeing a promise inside
A promise is created like this:
The promise constructor takes one argument: a callback with two parameters (resolve and reject). Then something is done with the callback (perhaps an asynchronous process) and resolve is called if all goes well, or reject if it doesn't.
As in throw of the JavaScript that we all know, it is customary (although not mandatory) to apply reject with an Error object. The advantage of Error objects is that they capture a stack trace; in this way, the debugging tools are more useful.
To use this promise:
then()
takes two arguments: one callback for success and one callback for failure. Both are optional; you can add a callback just for success or failure.Basic use of Promises
I think of this part as a kind of Promise by the example , to show some cases and examples of use of promises.
The constructor
new Promise()
should only be used for legacy asynchronous tasks, such as usingsetTimeout
orXMLHttpRequest
. A new promise is created with the new keyword, and the promise provides resolve and reject functions to the provided callback:It is up to the developer to manually call on
resolve
orreject
within the body of the callback based on the result of their task. A realistic example would be convertingXMLHttpRequest
to a promise-based task:Sometimes it is not necessary to complete async tasks within the promise, if it is possible for an async action to be taken, however, returning a promise will be best so you can always count on a promise coming out of a given function. In that case, you can simply call
promise.resolve()
orpromise.reject()
without using the new keyword. For example:Since a promise is always returned, you can always use the
then
and methodscatch
on its return value.then
All Promise instances have a method
then
that allows us to react to the promise. The first callback method receives the result given by the callresolve()
:The callback fires when the promise resolves. You can also chain method callbacks:
Cada
then
recibe el resultado del valor de retorno anterior.Si una promesa ya se ha resuelto pero se vuelve a llamar, la devolución de llamada se disparará inmediatamente. Si la promesa es rechazada y usted llama entonces después del rechazo, el callback nunca se llama.
El callback
catch
se ejecuta cuando se rechaza la promesa:Lo que usted proporcione al método de rechazo depende de usted. Un patrón frecuente es enviar un error a la captura:
promise.all
Piense en los cargadores de JavaScript: hay momentos en los que se desencadenan múltiples interacciones asíncronas, pero sólo se quiere responder cuando se completan todos ellos - ahí es donde
promise.all
entra en juego. El métodopromise.all
toma una serie de promesas y dispara una devolución de llamada una vez todos están resueltos:Una forma perfecta de pensar en
Promise.all
es disparar múltiples solicitudes AJAX (via fetch) al mismo tiempo:Podrías combinar APIs como
fetch
yBattery API
, ya que ambas retornan promesa:Lidiar con el rechazo es, por supuesto, difícil. Si alguna promesa es rechazada el
catch
es lanzado en el primer rechazo:Promise.all
será super útil a medida que más APIs se mueven hacia promesas.Promise.race
Un caso de uso podría estar provocando una solicitud a una fuente primaria y una fuente secundaria (en caso de que la primaria o la secundaria no estén disponibles).
Compatibilidad con navegadores y polyfill
En la actualidad, ya existen implementaciones de promesas en los navegadores.
A partir de Chrome 32, Opera 19, Firefox 29, Safari 8 y Microsoft Edge, las promesas vienen habilitadas de forma predeterminada.
Consulta el polyfill si deseas que los navegadores sin implementaciones completas de promesas cumplan con las especificaciones, o si quieres agregar promesas a otros navegadores y Node.js.
Compatibilidad con otras bibliotecas
La API de las promesas de JavaScript tratará a todos los elementos con un método
then()
como si fueran promesas (o thenable, si se usa el idioma de las promesas). Por lo tanto, no habrá problema si usas una biblioteca que muestra promesas; funcionará bien con las nuevas promesas de JavaScript.A pesar de que, como ha dicho, los Deferreds de jQuery son un poco inútiles. Afortunadamente, puedes transmitirlos a las promesas convencionales. Vale la pena hacerlo lo más pronto posible.
Ejemplo:
En este caso,
$.ajax
de jQuery muestra un elemento Deferred. Ya que tiene un métodothen(), Promise.resolve()
puede convertirlo en una promesa de JavaScript. Sin embargo, algunos deferreds pasan varios argumentos a sus callbacks, por ejemplo:En cambio, las promesas de JS ignoran todos menos el primero:
Afortunadamente, esto suele ser lo que quieres o, al menos, te brinda acceso a lo que quieres. Además, ten en cuenta que jQuery no sigue la convención de pasar objetos Error a rechazos.
Conclusión
Las promesas han sido un tema candente para los últimos años, y han pasado de un patrón de framework de JavaScript a un elemento básico del idioma. Iremos viendo cómo la mayoría de las nuevas API JavaScript se implementarán con un patrón basado en la promesa ...
...and that's a great thing! Thanks to promises, developers will be able to avoid callback hell and asynchronous interactions can be passed like any other variable. It may take a bit of getting used to using them, but the tools are already at hand, as they are native to most modern browsers. Now is the time to learn how to use them!
Links