Guía básica para JavaScript Promises
Guía básica para JavaScript Promises
La Guía básica para JavaScript Promises es una nueva adición a ECMAscript 6 que tiene como objetivo proporcionar un producto de limpieza, de manera más intuitiva para hacer frente a la terminación (o fracaso) de las tareas asíncronas.
Hasta JavaScript Promises, ese trabajo es asumida por los manipuladores de eventos de JavaScript (es decir:image.onload
) y funciones de devolución de llamada, popularizado por las bibliotecas como jQuery y Node.js, con diversos grados de frustración.
Los controladores de eventos funcionan bien con elementos individuales, pero lo que si querían, por ejemplo, ser notificado cuando una colección de imágenes han sido todos cargado o el orden en que sucedieron?
Volver a llamar funciones que se pasa como el último parámetro a los métodos que lo apoyan, como jQuery animado ()
la función de realizar su trabajo admirablemente para ejecutar código personalizado cuando una tarea se ha completado, pero lo que si el código personalizado en sí mismo también tiene que llamaranimado ( )
con otra función de devolución de llamada, y así sucesivamente?
Se termina con lo que se llama “el infierno de devolución de llamada”, o una pila creciente de devolución de llamada funciones parecidas a la Torre de Babel.
Promises JavaScript proporcionan un mecanismo para el seguimiento del estado de una tarea asíncrona con más robustez y menos caos. Pero lo primero es primero.
JavaScript promete apoyo y Polyfill
Las promesas JavaScript son parte de los estándares de ECMAScript 6 y deben ser compatibles con todos los navegadores con el tiempo.
En el momento en que la promesa ya se realiza en las últimas versiones de Chrome, FF, Safari, y en los navegadores móviles con la excepción de IE. Echa un vistazo a esta página para el flaco.
Debido a la ausencia de IE incluyendo IE11 en la columna verde, se puede utilizar un Polyfill como ES6-promise.js para llenar el vacío hasta IE pone al día con el resto de la manada.
Sólo tiene que descargar ES6-promesa-min.js e incluirlo en la parte superior de la página como un JavaScript externo, y la viola!
La sintaxis
Ok, vamos a ir al grano ahora. En el corazón de las promesas de JavaScript es la función constructora Promise, que se llama así:
1
2
3
4
5
|
var mypromise = nueva promesa ( función (resolver, rechazar) { // Código asíncrono para ejecutar aquí // Llamada de determinación () para indicar tarea completada con éxito // Rechazo de llamadas () para indicar las tareas ha fallado }) |
Se pasa una función anónima con dos Parámetros- una determinación ()
método que se llama en algún momento para establecer el estado de la promesa de cumplirse , y rechazan ()
para establecerlo en rechazada en su lugar.
Un objeto promesa comienza con un estado de espera de , para indicar el código asíncrono es el seguimiento ni ha completado (cumplido) o no (rechazado). Deja para conseguir nuestra primera muestra de JavaScript promesas en acción con una función que carga dinámicamente una imagen basada en la URL de la imagen:
1
2
3
4
5
6
7
8
9
10
11
12
|
función getImage (url) { volver nueva promesa ( función (resolución, rechazar) { var img = nueva imagen () img.onload = función () { determinación (url) } img.onerror = función () { rechazar (url) } img.src = url }) } |
El getImage ()
devuelve un objeto de la promesa que realiza un seguimiento del estado de la carga de la imagen. Cuando usted llama:
1
|
getImage ( 'doggy.gif' ) |
su objeto promesa va desde el estado inicial de “pendiente” a cualquiera cumplió o rechazada finalmente en función del resultado de la carga de la imagen.
Observe cómo hemos pasado la URL de la imagen que tanto la resolución ()
y rechazo ()
método de la promesa; esto podría ser cualquier datos que desea procesar adicionalmente en función del resultado de la tarea.
Ok, en este punto promesas solo pueden parecer un ejercicio inútil para establecer el estado de un objeto para indicar el estado de una tarea.
Pero, como pronto veremos, con este mecanismo viene la capacidad de definir fácilmente y de manera intuitiva lo que sucede a continuación, una vez que se complete la tarea.
El entonces () y la captura () métodos
Cada vez que se instancia un objeto de la promesa, dos métodos- entonces ()
y captura ()
– estén disponibles para decidir lo que sucede a continuación después de la conclusión de una tarea asincrónica. Echar un vistazo a el siguiente:
1
2
3
|
getImage ( 'doggy.jpg' ) .then ( función (successurl) { document.getElementById ( 'doggyplayground' ) .innerHTML = '<img src = "' + successurl + '" />' }) |
Aquí tan pronto como “doggy.jpg” se ha cargado, se especifica que la imagen se mostrará dentro de la ” doggyplayground
” DIV. El original getImage ()
devuelve un objeto de la promesa, por lo que podemos llamar entonces ()
en él para especificar lo que sucede cuando la solicitud se ha resuelto .
La URL de la imagen que pasamos en la determinación ()
función cuando creamos getImage ()
vuelve a estar disponible como parámetro dentro de la continuación ()
función.
¿Qué ocurre si la imagen no se pudo cargar? El entonces ()
método puede aceptar una segunda función para hacer frente a la rechazada estado del objeto promesa:
1
2
3
4
5
6
7
8
|
getImage ( 'doggy.jpg' ) .then ( función (successurl) { document.getElementById ( 'doggyplayground' ) .innerHTML = '<img src = "' + successurl + '" />' }, función (errorurl) { console.log ( 'Error al cargar' + errorurl) } ) |
En una construcción de este tipo, si se carga la imagen, la primera función dentro de entonces ()
se ejecuta, si falla, el segundo lugar.
También podemos controlar los errores utilizando el retén ()
método en su lugar:
1
2
3
4
5
|
getImage ( 'doggy.jpg' ) .then ( función (successurl) { document.getElementById ( 'doggyplayground' ) .innerHTML = '<img src = "' + successurl + '" />' }). Atrapar ( función (errorurl) { console.log ( 'Error al cargar' + errorurl) }) |
Llamando a la captura ()
es equivalente a llamar a continuación (no definido, la función)
, lo que lo anterior es lo mismo que:
1
2
3
4
5
|
getImage ( 'doggy.jpg' ) .then ( función (successurl) { document.getElementById ( 'doggyplayground' ) .innerHTML = '<img src = "' + successurl + '" />' }). entonces (no definido, la función (errorurl) { console.log ( 'Error al cargar' + errorurl) }) |
El uso de la recursividad para cargar y mostrar imágenes de forma secuencial
Digamos que tenemos una serie de imágenes que desea cargar y sequentially- pantalla, es decir, los primeros imagen1 carga y demostración, y una vez que esté completo, vamos a imagen2, y así sucesivamente.
Vamos a hablar de encadenar promesas juntos más adelante para lograr esto, pero un enfoque es sólo para usar la recursividad para pasar por la lista de imágenes, llamando a nuestro getImage ()
funcionar cada vez con un entonces ()
método que muestra la imagen actual antes llamandogetImage ()
de nuevo hasta que todas las imágenes han sido procesadas.
Aquí está el código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dieciséis
17
18
19
|
var doggyplayground = document.getElementById ( 'doggyplayground' ) var perritos = [ 'dog1.png' , 'dog2.png' , 'dog3.png' , 'dog4.png' , 'dog5.png' ] función displayimages (imágenes) { var targetimage = images.shift () // proceso de perritos imágenes de una en una si (targetimage) { // si no es el final de la serie getImage (targetimage) .then ( función (url) { // cargar la imagen a continuación ... var perro = document.createElement ( 'img' ) dog.setAttribute ( 'src' , url) doggyplayground.appendChild (perro) // agregar la imagen al DIV displayimages (imágenes) // displayimages llamada recursion- () de nuevo para procesar la imagen siguiente / perrito }). Atrapar ( función (url) { // manejar una imagen que no se cargan console.log ( 'Error al cargar' + url) displayimages (imágenes) // displayimages llamada recursion- () de nuevo para procesar la imagen siguiente / perrito }) } } displayimages (perritos) |
Los displayimages ()
la función toma una serie de imágenes y de forma secuencial pasa a través de cada imagen, llamando images.shift ()
.
Para cada imagen, lo primero que llamamos getImage ()
para buscar la continuación del objeto promesa de regresar imagen, entonces ()
método para especificar lo que sucede a continuación, en este caso, añadir la imagen a la doggyplayground
DIV antes de llamar displayimages ()
de nuevo.
En el caso de una imagen en su defecto a la carga, la captura ()
método se encarga de los casos. La recursividad se detiene cuando la matriz está vacía perritos, después Array.shift ()
ha pasado a través de todos sus elementos.
El uso de la recursividad con promesas de JavaScript es una forma de proceso secuencialmente una serie de tareas asíncronas. Otro método más versátil es aprendiendo el arte de encadenar promesas. Vamos a ver lo que se trata todo esto ahora.
Las promesas de encadenamiento
Ya sabemos que la continuación ()
método puede ser invocada en una instancia promesa para especificar lo que sucede después de la finalización de una tarea.
Sin embargo, podemos en múltiples cadena de hecho entonces ()
métodos juntos, a su vez, el encadenamiento de múltiples promesas juntos, para especificar lo que sucede después de cada promesa se ha resuelto, en secuencia.
Usando nuestra confianza getImage ()
la función de ilustrar, la siguiente obtiene una imagen antes de ir a buscar otra:
1
2
3
4
5
6
7
8
9
10
|
getImage ( 'dog1.png' ) .then ( función (url) { console.log (url + 'descabellada!' ) volver getImage ( 'dog2.png' ) }). entonces ( función (url) { console.log (url + 'descabellada!' ) }) // Registro de la consola: // Dog1.png descabellada // Dog2.png descabellada! |
Entonces, ¿qué está pasando aquí? Observe el interior del primer entonces ()
método, la línea:
1
|
volver getImage ( 'dog2.png' ) |
Esto obtiene “dog2.png” y devuelve un objeto de la promesa.
Al devolver un objeto dentro de Promise entonces ()
, el siguiente a continuación ()
espera a que la promesa de resolver antes de correr, aceptando como su parámetro de los datos transmitidos por el nuevo objeto promesa.
Esta es la clave para encadenar múltiples promesas juntos- devolviendo otra promesa dentro de la continuación ()
método.
Tenga en cuenta que también puede simplemente devolver un valor estático dentro de entonces ()
, que simplemente se realizaría en y ejecutado inmediatamente por la siguiente entonces ()
método como su valor de parámetro.
Con el ejemplo anterior todavía queremos dar cuenta de una imagen no se carga, por lo que vamos a incluir la captura ()
método, así:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
getImage ( 'baddog1.png' ) .then ( función (url) { console.log (url + 'descabellada!' ) }). Atrapar ( función (url) { console.log (url + 'no se pudo cargar!' ) }). entonces ( la función () { volver getImage ( 'dog2.png' ) }). entonces ( función (url) { console.log (url + 'descabellada!' ) }). Atrapar ( función (url) { console.log (url + 'no se pudo cargar!' ) }) // Registro de la consola: // Baddog1.png no se pudo cargar! // Dog2.png descabellada! |
Recordemos que la captura ()
es sinónimo de entonces (no definido, functionref)
, así que después de la captura ()
la siguiente entonces ()
todavía se ejecutará.
Nótese la organización de la continuación ()
y captura ()
métodos- ponemos la devolución del objeto siguiente promesa (o eslabón de la cadena) dentro de su propia continuación ()
método, después de que el resultado de la promesa anterior está completamente representado a través la continuación ()
y captura ()
método de proceder ella.
Si usted quiere cargar 3 imágenes en sucesión, por ejemplo, podríamos añadir otra serie de entonces ()
entonces ()
de captura ()
con el código de seguridad.
Creación de una secuencia de promesas
Ok, por lo que sabemos que la idea básica de encadenar juntos promesas es volver otra promesa dentro de la continuación ()
método.
Pero encadenar manualmente promesas juntos rápidamente puede llegar a ser inmanejable.
Para cadenas más largas, lo que necesitamos es una manera de empezar con un objeto promesa vacía y programación montón en el que desee a continuación ()
y captura ()
métodos para formar la secuencia final de promesas.
En Promesas de JavaScript, podemos crear un objeto de la promesa en blanco que se resuelve , para empezar con la línea:
1
|
var resolvedPromise = Promise.resolve () |
También hay Promise.reject ()
para crear un objeto de la promesa en blanco que ya está en el estado rechazado.
Así que ¿por qué queremos un nuevo objeto promesa que ya está resuelto usted puede pedir? Bueno, lo convierte en un objeto promesa perfecta de la cadena de promesas adicionales juntos, ya que un objeto Promise ya resuelto saltará automáticamente a la primera entonces ()
método añadido a la misma, y poner en marcha la cadena de acontecimientos.
Podemos utilizar un objeto Promise resolvió la creación de una secuencia de promesas, apilando en la continuación ()
y captura ()
métodos para ello. Por ejemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dieciséis
17
18
19
|
var secuencia = Promise.resolve () var perritos = [ 'dog1.png' , 'dog2.png' , 'dog3.png' , 'dog4.png' , 'dog5.png' ] doggies.forEach ( función (targetimage) { secuencia = sequence.then ( función () { volver getImage (targetimage) }). entonces ( función (url) { console.log (url + 'descabellada!' ) }). Atrapar ( función (err) { console.log (err + 'no se pudo cargar!' ) }) }) // Registro de la consola: // Dog1.png descabellada // Dog2.png descabellada! // Dog3.png descabellada! // Dog4.png descabellada! // Dog5.png descabellada! |
Primero creamos un objeto de la promesa resuelta llamada secuencia
, y luego ir a través de cada elemento dentro de los perritos []
array con forEach ()
, añadiendo a secuenciar
el requerido entonces ()
y captura ()
métodos para manejar cada imagen después de que se ha cargado.
El resultado es una serie de entonces ()
y de captura ()
métodos unidos a la secuencia
, la creación de la línea de tiempo deseado de la carga de cada imagen de una en una.
En caso de que se esté preguntando, en lugar de utilizar forEach ()
para desplazarse a través de la matriz de imagen, también puede utilizar un sencillo de
bucle lugar, aunque el resultado puede ser más de lo que había contado:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dieciséis
17
18
19
20
21
22
|
var secuencia = Promise.resolve () var perritos = [ 'dog1.png' , 'dog2.png' , 'dog3.png' , 'dog4.png' , 'dog5.png' ] para ( var i = 0; i <doggies.length; i ++) { ( Función () { // definir cierre para capturar i en cada paso del bucle var capturedindex = i secuencia = sequence.then ( función () { volver getImage (perritos [capturedindex]) }). entonces ( función (url) { console.log (url + 'descabellada!' ) }). Atrapar ( función (err) { console.log ( 'Error al cargar' + err) }) } ()) // Invocar la función de cierre de inmediato } // Registro de la consola: // Dog1.png descabellada // Dog2.png descabellada! // Dog3.png descabellada! // Dog4.png descabellada! // Dog5.png descabellada! |
Dentro de la de
bucle, para obtener adecuadamente el valor de i
en cada paso y pasarlo a continuación ()
, tenemos que crear un cierre exterior para capturar cada valor de i
.
Sin el cierre exterior, el valor de i
pasó a continuación ()
cada vez será simplemente el valor de i
cuando se llega al final del bucle, o doggies.length-1
. Si todo esto te confunde, el artículo cierres de JavaScript en bucles para deberían ayudar a limpiar el aire.
La creación de una serie de promesas
En lugar de encadenar promesas juntos, también podemos crear una serie de promesas.
Esto hace que sea fácil de hacer algo después de todas las tareas asíncronas han completado, en lugar de después de cada tarea.
Por ejemplo, los siguientes usos getImage ()
se ha podido recuperar dos imágenes y guardarlas como una serie de promesas:
1
|
var twodoggypromises = [getImage ( 'dog1.png' ), getImage ( 'dog2.png' )] |
Desde getImage ()
cuando se le vuelve una promesa, twodoggypromises
ahora contiene dos objetos promesa. Entonces, ¿qué podemos hacer con una serie de promesas?
Bueno, entonces podemos utilizar el método estático Promise.all ()
para hacer algo después de que todas las promesas encontradas en la matriz han resuelto:
1
2
3
4
5
|
Promise.all (twodoggypromises) .A continuación ( de función (URL) { console.log (URL) registros // [ 'dog1.png', 'dog2.png'] }). Atrapar ( de función (URL) { // Si alguna imagen no se carga, entonces () se omite y la captura se llama console.log (URL) // devuelve conjunto de imágenes que no se pudieron cargar }) |
Promise.all ()
toma un iterable (matriz o lista de arreglo similar) de los objetos de la promesa, y espera hasta que todas esas promesas se han cumplido antes de pasar a cualquier entonces ()
método que se le atribuye. El entonces ()
método se pasa una matriz de valores devueltos por cada promesa.
Entonces, ¿qué ocurre si una de las promesas encontradas en la matriz no resuelve (rechazada)? En ese caso, la totalidad de entonces ()
de la porción es ignorar, ycaptura()
se ejecuta en su lugar.
Así que en el escenario anterior, si una o más de las imágenes no se carga, sólo se registra una serie de imágenes que no se pudo cargar el interior de captura ()
.
Viendo las imágenes cuando todos ellos han sido extraídas
Ya es tiempo ahora para ver un ejemplo de mostrar todos los perritos cuando han sido extraídas, en lugar de a uno por vez. Vamos a utilizar Array.map ()
para aliviar el dolor en la creación de una matriz promesa:
1
2
3
4
5
6
7
8
9
10
11
12
|
var perritos = [ 'dog1.png' , 'dog2.png' , 'dog3.png' , 'dog4.png' , 'dog5.png' ] var doggypromises = doggies.map (getImage) // llamar getImage en cada elemento de la matriz y la matriz de las promesas de regreso Promise.all (doggypromises) .A continuación ( de función (URL) { para ( var i = 0; i <urls.length; i ++) { var perro = document.createElement ( 'img' ) dog.setAttribute ( 'src' , las direcciones URL [i]) doggyplayground.appendChild (perro) } }). Atrapar ( de función (URL) { console.log ( "Error al buscar algunas imágenes:" + URL) }) |
Array.map ()
itera a través de la matriz original y pide getImage ()
en cada elemento, el retorno de una nueva matriz que utiliza el valor de retorno de getImage ()
en cada paso, o una promesa.
El resultado es twofold- cada imagen se tomó, y, a su vez volvamos una serie de promesas correspondientes. A continuación, ponemosPromise.all ()
funcione, que pasa en doggypromises
para mostrar todas las imágenes a la vez
Ir a buscar imágenes de una sola vez, sino mostrar a continuación, en secuencia, ya que cada uno está lista
Por último, como si los perros no han sido lo suficientemente desfilado, los deja introducir al parque de perro de una manera optimizada, no uno por uno, no todos a la vez, pero lo mejor de ambas palabras.
Vamos a obtener todos los de las imágenes a la vez para tomar ventaja de la descarga en paralelo en los navegadores, pero les mostramos de forma secuencial, ya que cada uno esté disponible (exagerado).
Esto reduce al mínimo el tiempo de los perritos aparecen al mismo tiempo que muestra en una secuencia ordenada.
Para ello, sólo tenemos que hacer dos cosas que ya conocimientos creamos una serie de promesas a buscar todas las imágenes a la vez (en paralelo), a continuación, crear una secuencia de promesas para mostrar cada imagen en realidad uno a la vez:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var perritos = [ 'dog1.png' , 'dog2.png' , 'dog3.png' , 'dog4.png' , 'dog5.png' ] var doggypromises = doggies.map (getImage) // llamar getImage en cada elemento de la matriz y la matriz de las promesas de regreso var secuencia = Promise.resolve () doggypromises.forEach ( función (curPromise) { // crear la secuencia de promesas para actuar sobre cada uno de ellos en la sucesión secuencia = sequence.then ( función () { volver curPromise }). entonces ( función (url) { var perro = document.createElement ( 'img' ) dog.setAttribute ( 'src' , url) doggyplayground.appendChild (perro) }). Atrapar ( función (err) { console.log (err + 'no se pudo cargar!' ) }) }) |
Tenga en cuenta que para crear nuestra secuencia de las promesas de este tiempo, iteramos a través de la serie de promesas generadas por Array.map ()
, y no el conjunto de imágenes directamente.
Esto nos permite crear la cadena de promesas sin tener que llamar getImage ()
cada vez más, lo que se hizo cuando nos decidimos a buscar todas las imágenes a la vez utilizando Array.map ()
ya.
En conclusión
JavaScript Promises ofrece una forma adicional para manejar las tareas asíncronas en un momento en que dichas tareas se están íntimamente entretejidos en la tela de cualquier sitio web moderno.
Utilizado conjuntamente con un Polyfill , Promesas de JavaScript se pueden poner a trabajar hoy para hacer todo el asunto más intuitivo y manejable.
Con suerte este tutorial ha abierto las puertas para mostrar cómo sólo para hacer eso. Echa un vistazo a los siguientes recursos adicionales para ver más información sobre la nueva característica
Si quieres seguir aprendiendo con nosotros, puedes ingresar a nuestros