Este artículo populariza principalmente el uso de promesas.
Durante mucho tiempo, JavaScript siempre ha manejado asincrónicamente en forma de devolución de llamada, y el mecanismo de devolución de llamada en el campo del desarrollo front-end está casi profundamente arraigado en los corazones de las personas. Al diseñar API, ya sea un fabricante de navegador, desarrollador de SDK o autor de varias biblioteca, básicamente siguen la rutina de devolución de llamada.
En los últimos años, con la madurez gradual del modelo de desarrollo de JavaScript, ha nacido la especificación CommonJS, incluida la propuesta de la especificación de la promesa. Promise ha cambiado por completo la escritura de la programación asincrónica JS, haciendo que la programación asíncrona sea muy fácil de entender.
En el modelo de devolución de llamada, suponemos que se debe ejecutar una cola asíncrona, y el código puede verse así:
loadimg ('a.jpg', function () {loadImg ('b.jpg', function () {loadImg ('c.jpg', function () {console.log ('todo hecho!');});});});Esto es lo que a menudo llamamos a la pirámide. Cuando hay muchas tareas asincrónicas, mantener una gran cantidad de devoluciones de llamada será un desastre. Hoy en día, Node.js es muy popular. Parece que muchos equipos quieren usarlo para hacer algo para llevarse bien con la "moda". Una vez que chatear con un compañero de clase de operación y mantenimiento, también planearon usar Node.js para hacer algo, pero cuando piensan en las capas de devoluciones de llamada de JS, se desaniman.
Ok, las tonterías han terminado, vamos al tema.
La promesa puede estar familiarizada con todos, porque la especificación de la promesa ha estado fuera durante mucho tiempo, y la promesa se ha incluido en ES6, y las versiones más altas de los navegadores Chrome y Firefox se han implementado de forma nativa, pero hay menos API que la biblioteca de clases de promesa popular hoy en día.
La llamada promesa puede entenderse literalmente como "promesa", lo que significa que A las llamadas B, B devuelve una "promesa" a A, y luego A puede escribir esto al escribir el plan: cuando B me devuelve el resultado, un plan de ejecución S1. Por el contrario, si B no le da al resultado deseado por alguna razón, entonces un plan de emergencia S2, por lo que todos los riesgos potenciales están dentro del rango controlable de A.
La oración anterior se traduce en código similar a:
var resb = b (); var runa = function () {resb.then (ejecuts1, ejecuts2);}; runa ();Solo mirando la línea de código anterior, parece que no hay nada especial. Pero la realidad puede ser mucho más complicada que esto. Para lograr una cosa, A puede confiar en la respuesta de más de una persona B. Puede requerir preguntar a varias personas al mismo tiempo y luego implementar el siguiente plan después de recibir todas las respuestas. La traducción final al código puede verse así:
var resb = b (); var resc = c (); ... var runa = function () {reqb .then (rescate, ejecuts2) .then (resd, ejecuts3) .then (rese, ejecuts4) ... .then (ejecuts1);}; runa ();Aquí, se utilizan diferentes mecanismos de procesamiento cuando cada consulta responde que no están en línea con las expectativas. De hecho, la especificación de la promesa no requiere esto, e incluso puede hacer nada (es decir, no pasar en el segundo parámetro de entonces) o manejarlo de manera uniforme.
Ok, conozcamos las especificaciones de la promesa/A+:
then (se puede decir que entonces es el núcleo de la promesa), y luego debe devolver una promesa. Entonces de la misma promesa se puede llamar varias veces, y la orden de ejecución de las devoluciones de llamada es consistente con el orden cuando se definenComo puede ver, no hay mucho contenido en la especificación de la promesa, por lo que puede intentar implementar la siguiente promesa usted mismo.
La siguiente es una implementación simple de una promesa a la que he hecho referencia a muchas bibliotecas prometedoras. Pasa a Promisea en el código.
Un breve análisis de las ideas:
La promesa del constructor acepta un resolver de funciones, que puede entenderse como una tarea asincrónica. El resolución acepta dos parámetros, uno es una devolución de llamada cuando tiene éxito y el otro es una devolución de llamada cuando falla. Estos dos parámetros son iguales a los parámetros pasados en ese momento.
El segundo es la implementación de entonces. Dado que la promesa requiere que entonces deba devolver una promesa, se generará una nueva promesa cuando se llame, que se colgará en _next de la promesa actual. Múltiples llamadas de la misma promesa solo devolverán el _next generado previamente.
Dado que los dos parámetros aceptados por el método entonces son opcionales y no hay restricción en el tipo, puede ser una función, un valor específico u otra promesa. Aquí está la implementación específica de entonces:
Promet.prototype.then = function (resolve, rechazar) {var next = this._next || (this._next = promise ()); estado var = this.status; var x; if ('pending' === status) {isfn (resolve) && this._resolves.push (resolve); isfn (rechazar) && this._rejects.push (rechazar); regresar a continuación; } if ('resuelto' === status) {if (! isfn (resolve)) {next.resolve (resolve); } else {try {x = resolve (this.Value); resolvex (siguiente, x); } catch (e) {this.reject (e); }} return a continuación; } if ('rechazado' === status) {if (! isfn (rechazar)) {next.reject (rechazar); } else {try {x = rechazar (this.Rason); resolvex (siguiente, x); } catch (e) {this.reject (e); }} return a continuación; }};Aquí, ha simplificado la implementación de otras bibliotecas de clases de promesa, y la implementación es mucho más compleja que esto, y también tiene más funciones. Por ejemplo, hay un tercer parámetro: notificar, que indica el progreso actual de la promesa, que es muy útil al cargar archivos de diseño, etc. El procesamiento de varios parámetros de entonces es la parte más complicada. Los estudiantes interesados pueden referirse a la implementación de otros tipos de bibliotecas prometedoras.
Sobre la base de entonces, al menos se deben necesitar al menos dos métodos, a saber, completar la conversión del estado de la promesa de pendiente a resuelto o rechazado, y ejecutar la cola de devolución de llamada correspondiente, a saber, los métodos resolve() y reject() .
En este punto, se ha diseñado una promesa simple. Las siguientes son implementaciones simples de las siguientes dos funciones prometidas:
function sleep (ms) {return function (v) {var p = promise (); setTimeOut (function () {p.resolve (v);}, ms); regreso p; };}; función getImg (url) {var p = promise (); var img = nueva imagen (); img.onload = function () {p.resolve (this); }; img.onerror = function (err) {p.reject (err); }; img.url = url; return p;}; Dado que el constructor de promesas acepta una tarea asincrónica como parámetro, getImg también puede llamarse así:
función getImg (url) {return promise (function (resolve, rechazar) {var img = new image (); img.onload = function () {resolve (this);}; img.onerror = function (err) {rechazar (err);}; img.url = url;});};A continuación (el momento de presenciar el milagro), suponga que existe un requisito de BT para implementar esto: obtener una configuración JSON asincrónicamente, analizar los datos JSON y obtener las imágenes dentro, y luego cargar las imágenes en secuencia y dar un efecto de carga cuando no se cargue ninguna imagen.
function addImg (img) {$ ('#list'). find ('> li: último-child'). html (''). append (img);}; function prepend () {$ ('<li>') .html ('cargar ...') .AppendTo ($ ('#list'));}; function run () {$ ('####won'). getData ('map.json') .then (function (data) {$ ('h4'). html (data.name); return data.list.reduce (function (promise, item) {return. then (prepend) .then (sleep (1000)) .Then (function () {return getimg (item.url);}) .then (addimg);} Promise.resolve ());El sueño aquí se acaba de agregar para ver el efecto, ¡puede hacer clic para ver la demostración! Por supuesto, el ejemplo node.js se puede ver aquí.
Aquí, el método estático de Promise.resolve(v) simplemente devuelve una promesa con V como resultado positivo. V no se puede pasar, o puede ser una función o un objeto o función que contenga then el método (es decir, luego).
Los métodos estáticos similares incluyen Promise.cast(promise) , que genera una promesa con promesa como resultado positivo;
Promise.reject(reason) genera una promesa con razón como resultado negativo.
Nuestros escenarios de uso reales pueden ser muy complejos, y a menudo requieren múltiples tareas asincrónicas para ser ejecutadas de forma interconectada, paralela o en serie. En este momento, puede hacer varias extensiones para prometer, como implementar Promise.all() , aceptando la cola de promesas y esperando que se completen antes de continuar y, por ejemplo, Promise.any() , cuando cualquiera de las colas de promesas está en el estado de finalización, la próxima operación se activará.
Puede consultar este artículo en HTML5Rocks JavaScript Promeses. Actualmente, los navegadores avanzados como Chrome y Firefox tienen objetos de promesa incorporados, proporcionando más interfaces de operación, como Promise.all() , que admite pasar una matriz de promesas, y luego se ejecuta cuando se completan todas las promesas. También hay una captura de excepción más amigable y poderosa, que debería ser suficiente para lidiar con la programación asíncrona diaria.
Las populares bibliotecas JS hoy en día han implementado casi todas las promesas de diversos grados, como Dojo, JQuery, Zepto, When.js, Q, etc., pero la mayoría de los objetos expuestos Deferred . Tomar jQuery (Zepto similar) como ejemplo, implementa el getImg() anterior::
función getImg (url) {var def = $ .deferred (); var img = nueva imagen (); img.onload = function () {def.resolve (this); }; img.onerror = function (err) {def.reject (err); }; img.src = url; return def.promise ();}; Por supuesto, en jQuery, muchas operaciones regresan diferidas o prometidas, como animate y ajax :
// ajax $ .AJAX (Opacidad ': 0}, 1000) .promise () .Then (function () {console.log (' 'ded');}); // ajax $ .JAJAX (opciones) .Then (éxito, fail); $. AJAX (Opciones). $ .AJAX (OPTIONS2)) .THEN (function () {console.log ('todo hecho.');}, function () {console.error ('hay algo mal.');}); JQuery también implementa done() y fail() , que en realidad son atajos del método entonces.
Para manejar las colas de las promesas, jQuery implementa $.when() , y su uso es similar a Promise.all() .
Para otras bibliotecas de clase, vale la pena mencionar aquí que cuando.js tiene poco código, implementa completamente prometedor, admite navegador y node.js y proporciona API más ricas, lo cual es una buena opción. Debido a las limitaciones de espacio, ya no lo expandiremos.
Vemos que no importa cuán compleja sea la implementación de la promesa, su uso es muy simple y el código organizacional es muy claro. De ahora en adelante, no hay necesidad de ser torturado por la devolución de llamada.
¡Finalmente, la promesa es tan elegante! Pero la promesa solo resuelve el problema de la anidación profunda de las devoluciones de llamada. Es el generador que realmente simplifica la programación asíncrona de JavaScript. En el lado node.js, se recomienda considerar el generador.
Siguiente artículo, Generador de estudio.
Texto original de Github: https://github.com/chemdemo/chemdemo.github.io/issues/6