Prefacio
La primera vez que entré en contacto con Promise fue cuando Microsoft lanzó el sistema operativo Windows 8 en 2012 y estudié usando HTML5 para escribir aplicaciones de metro con una actitud curiosa. En ese momento, las interfaces asíncronas en la Biblioteca WinJS proporcionada con HTML5 estaban en forma prometedora, que era simplemente un libro del cielo para mí que acababa de graduarse de JavaScript en ese momento. Lo que estaba pensando en ese momento era que Microsoft estaba jugando nuevamente con eso.
Inesperadamente, para 2015, la promesa en realidad se escribió en el estándar ES6. Además, una encuesta muestra que los programadores de JS usan esto bastante alto.
Irónicamente, como Microsoft, que se usó ampliamente en la interfaz de desarrollo de aplicaciones de Metro ya en 2012, su propio navegador, IE, todavía no admitió prometer hasta que murió en 2015. Parece que Microsoft no tiene esta tecnología, pero realmente ha renunciado a un tratamiento para IE. . .
Mirando hacia atrás ahora, lo más problemático de ver la promesa en ese momento fue que los principiantes parecen increíbles y también es la característica más ampliamente elogiada por los programadores de JS: luego funcione la cadena de llamadas.
Luego, la cadena de llamadas de la función, esencialmente, es llamar múltiples procesos asincrónicos en secuencia. Este artículo comienza desde este punto y estudia y aprende la característica de la promesa.
Promesa resuelta
Considere el siguiente escenario, después de que la función se retrase en 2 segundos, imprima una línea de registros, luego se retrasa en 3 segundos y luego se retrasa en 4 segundos, imprima una línea de registros. Esto es algo muy simple en otros lenguajes de programación, pero es más difícil entrar en JS, y el código probablemente se escribirá de la siguiente manera:
var myFunc = function () {setTimeOut (function () {console.log ("log1"); setTimeOut (function () {console.log ("log2"); setTimeOut (function () {console.log ("log3");}, 4000);}, 3000);}, 2000);}Debido a la estructura de devolución de llamada multicapa anidada, aquí se forma una estructura piramidal típica. Si la lógica comercial es más complicada, se convertirá en un infierno de devolución de llamada aterrador.
Si tiene mejor conciencia y sabe cómo extraer funciones simples, entonces el código se verá así:
var func1 = function () {setTimeout (func2, 2000);}; var func2 = function () {console.log ("log1"); setTimeOut (func3, 3000);}; var func3 = function () {console.log ("log2"); setTimeOut (func4, 4000);}; var func4 = function () {console.log ("log3");};Esto se ve un poco mejor, pero siempre se siente un poco raro. . . Bueno, de hecho, mi nivel JS es limitado, así que no puedo decir por qué no puedo escribir esto bien. Si sabe por qué esto no es bueno y, por lo tanto, inventó la promesa, hágamelo saber.
Ahora volvamos al grano y hablemos sobre lo prometedor.
Descripción de la promesa
Permítame citar la descripción de la promesa de MDN aquí:
El objeto prometedor se usa para cálculos diferidos y cálculos asincrónicos. Un objeto de promesa representa una operación que no se ha completado pero se espera que se complete en el futuro.
El objeto de promesa es un proxy para el valor de retorno, que puede no ser conocido cuando se crea el objeto de promesa. Le permite especificar un método de manejo para el éxito o la falla de una operación asincrónica. Esto permite que un método asincrónico devuelva un valor como un método sincrónico: el método asíncrono devuelve un objeto prometedor que contiene el valor de retorno original en lugar del valor de retorno original.
El objeto de la promesa tiene los siguientes estados:
• Pendiente: estado inicial, no cumplido o rechazado.
• Cumplido: operación exitosa.
• Rechazado: operación fallida.
El objeto de promesa con un estado pendiente se puede convertir a un estado realizado con un valor de éxito o un estado rechazado con un mensaje de falla. Cuando el estado hace la transición, se llamará al método prometedor. Luego (manejo de la función) se llamará. (Al unir un método, si el objeto prometedor ya está en el estado cumplido o rechazado, el método correspondiente se llamará de inmediato, por lo que no existe una condición de carrera entre la finalización de la operación asincrónica y su método de unión).
Para obtener más descripciones y ejemplos de promesas, consulte la entrada de la promesa de MDN o la entrada de promesa de MSDN.
Intenta resolver nuestro problema con la promesa
Según la comprensión anterior de la promesa, sabemos que podemos usarlo para resolver el problema que el código detrás de las devoluciones de llamada de múltiples capas anidadas es estúpida y difícil de mantener. Los dos enlaces dados anteriormente ya están muy claros sobre la sintaxis y los parámetros de la promesa. No los repetiré aquí, solo sube el código.
Primero probemos un caso relativamente simple, que solo ejecuta retrasos y devoluciones de llamada una vez:
New Promise (function (res, rej) {console.log (date.Now () + "iniciar setTimeOut"); setTimeOut (res, 2000);}). Entonces (function () {console.log (date.now () + "timeout llame de nuevo");});Parece que no hay diferencia con los ejemplos en MSDN, y el resultado de la ejecución es el siguiente:
$ node promistest.js1450194136374 inicio setTimeout1450194138391 Tiempo de espera de retroceso
Entonces, si queremos hacer otro retraso, entonces puedo escribir esto:
New Promise (function (res, rej) {console.log (date.Now () + "inicio setTimeout 1"); setTimeOut (res, 2000);}). entonces (function () {console.log (date.now () + "timeout 1 llame a revertir"); nueva promesa (function (res, rej) {console.log (date.Now () + "setTimeOut 2"); 3000);Parece funcionar correctamente también:
$ node promistest.js1450194338710 inicio setTimeOut 11450194340720 Tiempo de espera 1 devolución de la llamada1450194340720 iniciar setTimeout 21450194343722 Tiempo de espera 2 Volver a la llamada
Pero el código se ve estúpido y lindo, ¿verdad? Está construyendo vagamente una pirámide nuevamente. Esto va en contra del propósito de introducir promesa.
Entonces, ¿cuál es el problema? ¿Cuál es la postura correcta?
La respuesta está oculta en el valor de retorno de la función entonces de la función y la función de devolución de llamada Onfulle (o OnCompleted).
En primer lugar, la función entonces devolverá una nueva variable de promesa, y puede llamar a la función entonces de esta nueva variable de promesa nuevamente, como esta:
nueva promesa (...). Entonces (...) .Then (...). Entonces (...). Entonces (...). Entonces (...).
Qué tipo de promedio es devuelta por la función entonces depende del valor de retorno de la devolución de llamada onfullida.
De hecho, OnfullIfled puede devolver una variable normal u otra variable de promesa.
Si OnfullIlled devuelve un valor normal, la función devolverá una variable de promesa predeterminada. Ejecución de la función entonces de esta promesa hará que la promesa satisfaga de inmediato, y la función onfullida se ejecuta, y el parámetro de entrada a la fullimida es el valor de retorno del anterior Onfullilled.
Si OnfullIlled devuelve una variable prometedora, esa variable de promesa se utilizará como el valor de retorno de la función entonces.
Los documentos en MDN y MSDN no tienen una descripción clara de esta serie de configuraciones para la función entonces y la función onfullida. En cuanto al documento oficial de ES6 ECMAScript 2015 (sexta edición, ECMA-262). . . Realmente no puedo entender mi nivel. Si algún experto puede explicar la descripción de los dos valores de retorno en el documento oficial, ¡deje un mensaje para obtener asesoramiento! ! !
Entonces lo anterior es mi juego gratuito, y la organización del idioma es un poco difícil de describir. Entenderá después de leer el código.
Primero, el caso de devolver variables normales:
New Promise (function (res, rej) {console.log (date.Now () + "Iniciar setTimeOut 1"); setTimeOut (res, 2000);}). Entonces (function () {console.log (date.now () + "timeout 1 devolte"); return 1024;}). luego (function (arg) {console.log (date.now () arg);El resultado de la ejecución del código anterior es:
$ node promistest.js1450277122125 inicio setTimeout 11450277124129 Tiempo de espera 1 Llama a la devolución1450277124129 Última devolución de folleto 1024
Es un poco interesante, correcto, pero esa no es la clave. La clave es que la función onfullida devuelve una variable de promesa, lo que hace que sea conveniente para nosotros llamar a múltiples procesos asíncronos en sucesión. Por ejemplo, podemos intentar hacer dos operaciones de retraso en sucesión:
New Promise (function (res, rej) {console.log (date.Now () + "inicio setTimeOut 1"); setTimeOut (res, 2000);}). entonces (function () {console.log (date.now () + "timeout 1 llame a la devolución"); return (function (res, rej) {console.log (date.Now () + "setTimeOut 2"); 3000);Los resultados de la ejecución son los siguientes:
$ node promistest.js1450277510275 inicio setTimeout 11450277512276 Tiempo de espera 1 devolución de la llamada1450277512276 inicio setTimeout 21450277515327 Tiempo de espera 2 devolución de llamada
Si crees que esto no es nada grandioso, entonces no es un problema hacerlo varias veces más:
New Promise (function (res, rej) {console.log (date.Now () + "inicio setTimeOut 1"); setTimeOut (res, 2000);}). entonces (function () {console.log (date.now () + "timeout 1 llame a la devolución"); return (function (res, rej) {console.log (date.Now () + "setTimeOut 2"); 3000);});}). Entonces (function () {console.log (date.Now () + "TimeOut 2 Llame a la devolución"); return New Promise (Function (Res, REJ) {Console.log (date.Now () + "Start SetTimeout 3"); SetTimeOut (Res, 4000);});}). "Tiempo de espera 3 devuelve");$ node promistest.js1450277902714 inicio setTimeOut 11450277904722 Tiempo de espera 1 devolución1450277904724 Inicio setTimeout 214502779077225 Tiempo de espera 2 Llame a la devolución14502790725 Inicio SetTimeOut 3145027911730 Tiempo de tiempo 3 CALIDA 3 setTimeout 41450277916744 Tiempo de espera 4 devolución de llamada
Se puede ver que múltiples funciones retrasadas de devolución de llamada se organizan de manera ordenada, y no existe una estructura popular similar a la pirámide. Aunque el código llama procesos asíncronos, parece que todos están compuestos por procesos sincrónicos. Esta es la promesa de beneficio nos trae.
Si tiene el buen hábito de destilar el código detallado en funciones separadas, será aún más hermoso:
function timeOut1 () {return new promise (function (res, rej) {console.log (date.now () + "start timeOut1"); setTimeOut (res, 2000);});} function timeout2 () {return new promise (function (res, rej) {console.log (date.now () + "inicio de tiempo de inicio2"); settimeOut (res, 3000);};};};};};};};} TimeOut3 () {return new promise (function (res, rej) {console.log (date.Now () + "Start TimeOut2"); setTimeOut (res, 3000);});} function timeout3 () {return new promise (function (res, rej) {console.log (date.now () + "start timeOut3"); settimeout (res, 4000);};};} TimeOut4 () {return new promise (function (res, rej) {console.log (date.Now () + "Start TimeOut4"); setTimeOut (res, 5000);});} timeout1 () .Then (TimeOut2) .Then (TimeOut3) .Then (TimeOut4) .Then (function () {Console.Log (date.Now () ") });$ nodo promistest.js1450278983342 Tiempo de espera de inicio11450278985343 Tiempo de espera de inicio2145027898351 Tiempo de espera de inicio31450278992356 Tiempo de espera de inicio41450278997370 Llamada de llamada
A continuación, podemos continuar estudiando el problema de pasar en los parámetros entrantes de la función onfullida.
Ya sabemos que si la función Onfulled -Onfulled anterior devuelve un valor normal, entonces este valor es el parámetro de entrada de la función onfullida; Entonces, si el anterior OnfullIfled devuelve una variable de promesa, ¿de dónde proviene el parámetro de entrada del Onfullfilled?
La respuesta es que el parámetro de entrada de esta función onfullida es el valor pasado cuando la función de resolución se llamó en la promesa anterior.
No pude aceptar el salto por un tiempo, ¿verdad? Hagámoslo bien.
En primer lugar, ¿cuál es la función promesa.resolve? Usando la declaración de Zou Zou por encima de MDN
Resuelva un objeto de promesa con el valor del valor de éxito. Si el valor es continuo (entonces, es decir, con el método entonces), el objeto prometido devuelto "seguirá" el valor
En resumen, esta es la devolución de llamada cuando la llamada asíncrona es exitosa.
Echemos un vistazo a cómo se ve la devolución de llamada en una interfaz asíncrona normal. Tome fs.ReadFile (archivo [, opciones], devolución de llamada) en NodeJS, por ejemplo. Su ejemplo típico de llamada es el siguiente
fs.ReadFile ('/etc/passwd', function (err, data) {if (err) throin err; console.log (data);});Porque para la función Fs.ReadFile, ya sea exitosa o fallida, llamará a la devolución de llamada de la función de devolución de llamada, por lo que esta devolución de llamada acepta dos parámetros, a saber, la descripción de la excepción en la falla ERR y los datos de los resultados de retorno sobre el éxito.
Entonces, si usamos la promesa de reconstruir este ejemplo de archivo de lectura, ¿cómo debemos escribirlo?
Primero, encapsule la función Fs.ReadFile:
function readFile (filename) {return new promise (function (resolve, rechazar) {fs.ReadFile (filename, function (err, data) {if (err) {rechazar (err);} else {resolve (data);}});});});};};El segundo es la llamada:
readFile ('thefile.txt'). entonces (function (data) {console.log (data);}, function (err) {throw err;});¿Imagina dónde se coloca el contenido del archivo en la interfaz de llamadas sincrónicas de leer archivos en otros idiomas? ¿El valor de retorno de la función es correcto? La respuesta está fuera, ¿cuál es la entrada ginseng de esta resolución? Es el valor de retorno cuando la llamada asíncrona es exitosa.
Con este concepto, no es difícil entender el "parámetro de entrada de la función onfullida es el valor que se pasa al llamar a la función de resolución en la promesa anterior". Porque la tarea onfullida es procesar el resultado después de que la llamada asincrónica anterior sea exitosa.
Por desgracia, finalmente se enderezó. . .
Resumir
Permítame usar un código para resumir los puntos clave explicados en este artículo:
función callp1 () {console.log (date.now () + "iniciar callp1"); return new Promise (function (res, rej) {setTimeout (res, 2000);});} function calp2 () {console.log (date.Now () + "iniciar callp2"); return new Promise (function (res, rej) {setTimeOut (function () {res ({arg1: 4, arg2: "arg2 valor"});}, 3000);});} function callp3 (arg) {console.log (date.Now () + "Iniciar Callp3 con arg =" + arg); return new Promise (function (res, rej) {setTimeOut (function () {res ("callp3");}, arg * 1000);});} callp1 (). entonces (function () {console.log (date.Now () + "llameP1 return"); return fallp2 ();}). entonces (function (retol value = " + json.stringify (ret)); return callp3 (ret.arg1);}). entonces (function (ret) {console.log (date.now () +" callp3 return con value = " + ret);}) $ node promistest.js1450191479575 iniciar callp11450191481597 callp1 return1450191481599 iniciar callp21450191484605 callp2 return with Ret value = {"arg1": 4, "arg2": "valor de arg2"} 1450191484605 inicio 41450191488610 Callp3 Return con Ret Value = Callp3La experiencia de aprendizaje simple anterior de usar Promise para resolver las llamadas asíncronas de varias capas es todo el contenido que comparto con usted. Espero que pueda darle una referencia y espero que pueda apoyar más a Wulin.com.