Préface
La première fois que je suis entré en contact avec Promise, c'est lorsque Microsoft a publié le système d'exploitation Windows 8 en 2012 et étudié en utilisant HTML5 pour écrire des applications de métro avec une attitude curieuse. À cette époque, les interfaces asynchrones de la bibliothèque Winjs fournies avec HTML5 étaient toutes en forme de promesse, qui était simplement un livre du ciel pour moi qui venait d'obtenir son diplôme de JavaScript à l'époque. Ce que je pensais à l'époque, c'est que Microsoft brillait à nouveau.
De façon inattendue, d'ici 2015, la promesse a en fait été écrite dans la norme ES6. De plus, une enquête montre que les programmeurs JS utilisent cette chose assez élevée.
Ironiquement, comme Microsoft, qui a largement utilisé la promesse dans l'interface de développement des applications de métro dès 2012, son propre navigateur, IE, n'a toujours pas pris en charge la promesse avant sa mort en 2015. Il semble que Microsoft n'a pas cette technologie, mais a vraiment abandonné le traitement pour IE. . .
En regardant en arrière maintenant, la chose la plus gênante à propos de la promesse à l'époque était que les débutants semblent incroyables et que c'est aussi la fonctionnalité la plus largement louée par les programmeurs JS: puis la chaîne d'appel de fonction.
Ensuite, la chaîne d'appels de fonction, essentiellement, est d'appeler plusieurs processus asynchrones en séquence. Cet article commence à partir de ce point et étudie et apprend la caractéristique de la promesse.
Promesse résolue
Considérez le scénario suivant, après la fonction de 2 secondes, imprimez une ligne de journaux, puis retarde de 3 secondes, puis des retards de 4 secondes, imprimez une ligne de journaux. C'est une chose très simple dans d'autres langages de programmation, mais il est plus difficile de pénétrer dans JS, et le code sera probablement écrit comme suit:
var myfunc = function () {setTimeout (function () {console.log ("log1"); setTimeout (function () {console.log ("log2"); setTimeout (function () {console.log ("log3");}, 4000);}, 3000);}, 2000);}En raison de la structure de rappel multicouche imbriquée, une structure pyramide typique est formée ici. Si la logique commerciale est plus compliquée, elle deviendra un enfer terrifiant.
Si vous avez une meilleure prise de conscience et savez comment extraire des fonctions simples, le code ressemblera à ceci:
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");};Cela a l'air un peu mieux, mais c'est toujours un peu bizarre. . . Eh bien, en fait, mon niveau JS est limité, donc je ne peux pas dire pourquoi je ne peux pas bien écrire cela. Si vous savez pourquoi ce n'est pas bon et que vous avez donc inventé la promesse, faites-le moi savoir.
Maintenant, revenons au point et parlons de la promesse.
Description de la promesse
Veuillez me permettre de citer la description de MDN de la promesse ici:
L'objet Promise est utilisé pour les calculs différés et les calculs asynchrones. Un objet de promesse représente une opération qui n'a pas été achevée mais qui devrait être achevée à l'avenir.
L'objet Promise est un indicateur indirect de la valeur de retour, qui peut ne pas être connu lorsque l'objet Promise est créé. Il vous permet de spécifier une méthode de traitement pour le succès ou la défaillance d'une opération asynchrone. Cela permet à une méthode asynchrone de renvoyer une valeur comme une méthode synchrone: la méthode asynchrone renvoie un objet de promesse contenant la valeur de retour d'origine au lieu de la valeur de retour d'origine.
L'objet de promesse a les états suivants:
• En attente: état initial, non rempli ou rejeté.
• Réalivé: fonctionnement réussi.
• Rejeté: fonctionnement échoué.
L'objet de promesse avec un état en attente peut être converti en un état rempli avec une valeur de réussite ou un état rejeté avec un message de défaillance. Lorsque l'état transitions, la méthode liée à la promesse. Puis (poignée de fonction) sera appelée. (Lors de la liaison d'une méthode, si l'objet Promise est déjà à l'état accompli ou rejeté, la méthode correspondante sera immédiatement appelée, il n'y a donc pas de condition de course entre l'achèvement de l'opération asynchrone et sa méthode de liaison.)
Pour plus de descriptions et d'exemples de promesses, veuillez vous référer à l'entrée de promesse de MDN ou à l'entrée de promesse de MSDN.
Essayez de résoudre notre problème avec la promesse
Sur la base de la compréhension ci-dessus de la promesse, nous savons que nous pouvons l'utiliser pour résoudre le problème que le code derrière les rappels multicouches imbriqués est stupide et difficile à maintenir. Les deux liens ci-dessus sont déjà très clairs sur la syntaxe et les paramètres de promesse. Je ne les répéterai pas ici, il suffit de télécharger le code.
Essayons d'abord un cas relativement simple, qui exécute une seule fois des retards et des rappels:
Nouvelle promesse (fonction (res, rej) {console.log (date.now () + "start setTimeout"); setTimeout (res, 2000);}). alors (fonction () {console.log (date.now () + "temps d'attente");});Il semble qu'il n'y ait pas de différence par rapport aux exemples de MSDN, et le résultat de l'exécution est le suivant:
$ node promest.js1450194136374 start setTimeout1450194138391 Appel à temps
Donc, si nous voulons faire un autre retard, je peux écrire ceci:
Nouvelle promesse (fonction (res, rej) {console.log (date.now () + "Démarrer setTimeout 1"); setTimeout (res, 2000);}). puis (fonction () {console.log (date.now () + "timeout 1 appel"); new promest (function (res, rej) {console.log (date.now () + "startTimet 2"); 3000);}). Ensuite (fonction () {console.log (date.Now () + "Timeout 2 Call Back");})});Cela semble aussi fonctionner correctement:
$ node promest.js1450194338710 Démarrer setTimeout 11450194340720 Timeout 1 rappel1450194340720 start setTimeout 21450194343722 Timeout 2 rappel
Mais le code a l'air stupide et mignon, non? C'est à nouveau vaguement construire une pyramide. Cela va à l'encontre du but d'introduire la promesse.
Alors, quel est le problème? Quelle est la bonne posture?
La réponse est cachée dans la valeur de retour de la fonction alors et la fonction de rappel sur la fonction de la fonction d'alors (ou ONCOMPleted).
Tout d'abord, la fonction alors renverra une nouvelle variable de promesse, et vous pouvez à nouveau appeler la fonction alors de cette nouvelle variable de promesse, comme ceci:
Nouvelle promesse (...). Ensuite (...). puis (...). Ensuite (...). Ensuite (...). Ensuite (...).
Le type de promotion est renvoyé par la fonction alors dépend de la valeur de retour du rappel à vie.
En fait, onfulful peut renvoyer une variable normale ou une autre variable de promesse.
Si onfulfild renvoie une valeur normale, la fonction renverra une variable de promesse par défaut. L'exécution de la fonction alors de cette promesse fera que la promesse satisfera immédiatement, et la fonction ondulée est exécutée, et le paramètre d'entrée à vie est la valeur de retour du précédent.
Si onfulfild renvoie une variable de promesse, cette variable de promesse sera utilisée comme valeur de retour de la fonction alors.
Les documents sur MDN et MSDN n'ont pas une description positive claire de cette série de paramètres pour la fonction alors et la fonction onfulllée. Quant au document officiel ECMAScript 2015 (6e édition, ECMA-262). . . Je ne comprends vraiment pas mon niveau. Si un expert peut expliquer la description des deux valeurs de retour dans le document officiel, veuillez laisser un message pour obtenir des conseils! ! !
Ce qui précède est mon jeu gratuit, et l'organisation linguistique est un peu difficile à décrire. Vous comprendrez après avoir lu le code.
Tout d'abord, le cas du retour des variables normales:
Nouvelle promesse (fonction (res, rej) {console.log (date.now () + "Démarrer setTimeout 1"); setTimeout (res, 2000);}). alors (fonction () {console.log (date.Now () + "Timeout 1 Appel"); return 1024;}). Ensuite (fonction (arg) {console.log (date.Now () + "Last onful arg);});Le résultat de l'exécution de code ci-dessus est:
$ node promest.js1450277122125 Démarrer setTimeout 11450277124129 Timeout 1 rappel1450277124129 Last Onfulllad Retour 1024
C'est un peu intéressant, non, mais ce n'est pas la clé. La clé est que la fonction ondulée renvoie une variable de promesse, ce qui nous permet d'appeler successivement les processus asynchrones. Par exemple, nous pouvons essayer de faire deux opérations de retard successivement:
nouvelle promesse (fonction (res, rej) {console.log (date.now () + "start setTimeout 1"); setTimeout (res, 2000);}). puis (fonction () {console.log (date.now () + "timeout 1 rappel"); return new promed (function (res, rej) {console.log (date.Now () + "start Settime 2"); 3000);});}). Alors (fonction () {console.log (date.Now () + "Timeout 2 Call Back");});Les résultats de l'exécution sont les suivants:
$ node promemit.js1450277510275 Démarrer setTimeout 11450277512276 Timeout 1 rappel1450277512276 Démarrer setTimeout 21450277515327 Timeout 2 rappel
Si vous pensez que ce n'est pas génial, alors ce n'est pas un problème de le faire plusieurs fois:
nouvelle promesse (fonction (res, rej) {console.log (date.now () + "start setTimeout 1"); setTimeout (res, 2000);}). puis (fonction () {console.log (date.now () + "timeout 1 rappel"); return new promed (function (res, rej) {console.log (date.Now () + "start Settime 2"); 3000);});}). Puis (fonction () {console.log (date.now () + "timeout 2 callack"); return new promed (function (res, rej) {console.log (date.now () + "start setTimeout 3"); "délai d'attente 3"); return new promed (function (res, rej) {console.log (date.now () + "start setTimeout 4"); setTimeout (res, 5000);});});}). puis (fonction () {console.log (date.now () + "délai de temps 4");});$ nœud provistist.js1450277902714 Démarrer setTimeout 11450277904722 Timeout 1 rappel1450277904724 start settimeout 21450277907725 Timeout 2 Rappel1450277907725 start settimeout 314502777911730 Settimeout 41450277916744 Timeout 4 rappel
On peut voir que plusieurs fonctions de rappel retardées sont organisées de manière ordonnée, et il n'y a pas de structure populaire de type pyramide. Bien que le code appelle des processus asynchrones, il semble qu'ils soient tous composés de processus synchrones. C'est l'avantage que la promesse nous apporte.
Si vous avez la bonne habitude de distiller le code verbeux en fonctions distinctes, ce sera encore plus beau:
Fonction Timeout1 () {return new promed (function (res, rej) {console.log (date.now () + "start timeout1"); setTimeout (res, 2000);});} function timeout2 () {return new promed (function (res, rej) {console.log (date.now () + "start timeout2"); settimeout (res, 3000);}} fure Timeout3 () {return new promers (function (res, rej) {console.log (date.now () + "start timeout2"); setTimeout (res, 3000);});} function timeout3 () {return new promed (function (res, rej) {console.log (date.now () + "start3out3"); settimeout (res, 4000);}); Timeout4 () {return new promers (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 () + "TiMout4; });$ nœud promist.js1450278983342 start timeout11450278985343 start timeout2145027898351 start timeout31450278992356 start timeout41450278997370 timout4 RAPPHAG
Ensuite, nous pouvons continuer à étudier le problème de la transmission dans les paramètres entrants de la fonction surfilée.
Nous savons déjà que si la fonction Onfulfillée précédente renvoie une valeur normale, cette valeur est le paramètre d'entrée de la fonction Onfulfillée; Ensuite, si le précédent Renvoie une variable de promesse, d'où vient le paramètre d'entrée de l'on
La réponse est que le paramètre d'entrée de cette fonction onfulchée est la valeur réalisée lorsque la fonction de résolution a été appelée dans la promesse précédente.
Je ne pouvais pas accepter le saut pendant un moment, non? Faisons bien.
Tout d'abord, quelle est la fonction de la fonction. Resolve? Utilisation de la déclaration de Zou Zou au-dessus de MDN
Résolvez un objet de promesse avec la valeur de valeur de réussite. Si la valeur est continue (alors, c'est-à-dire avec la méthode alors), l'objet de promesse renvoyé "suivra" la valeur
En bref, c'est le rappel lorsque l'appel asynchrone réussit.
Jetons un coup d'œil à quoi ressemble le rappel dans une interface asynchrone normale. Prenez fs.readfile (fichier [, options], rappel) sur NodeJS par exemple. Son exemple d'appel typique est le suivant
fs.readfile ('/ etc / passwd', fonction (err, data) {if (err) throw err; console.log (data);});Étant donné que pour la fonction FS.ReadFile, qu'elle soit réussie ou échouée, il appellera le rappel de la fonction de rappel, donc ce rappel accepte deux paramètres, à savoir la description de l'exception sur l'erreur de défaillance et les données de résultat de retour sur le succès.
Donc, si nous utilisons la promesse de reconstruire cet exemple de fichier de lecture, comment devons-nous l'écrire?
Tout d'abord, encapsulez la fonction Fs.ReadFile:
fonction readFile (filename) {return new promers (function (résoudre, rejeter) {fs.readfile (nom de fichier, fonction (err, data) {if (err) {rejeter (err);} else {résolve (data);}});});});};Le second est l'appel:
readFile ('thefile.txt'). puis (fonction (data) {console.log (data);}, fonction (err) {throw err;});Imaginez où le contenu du fichier est généralement placé dans l'interface d'appel synchrone des fichiers de lecture dans d'autres langues? La valeur de retour de la fonction est-elle correcte? La réponse est sortie, quel est le ginseng d'entrée de cette résolution? C'est la valeur de retour lorsque l'appel asynchrone réussit.
Avec ce concept, il n'est pas difficile de comprendre "le paramètre d'entrée de la fonction ondulée est la valeur réalisée lors de l'appel de la fonction de résolution dans la promesse précédente". Parce que la tâche à volonté est de traiter le résultat après le succès de l'appel asynchrone précédent.
Hélas finalement redressé. . .
Résumer
Veuillez me permettre d'utiliser un morceau de code pour résumer les points clés expliqués dans cet article:
fonction callp1 () {console.log (date.now () + "start callp1"); return new promed (function (res, rej) {setTimeout (res, 2000);});} function callp2 () {console.log (date.now () + "start callp2"); return new promed (function (res, rej) {setTimeout (function () {res ({arg1: 4, arg2: "arg2 value"});}, 3000);});} function callp3 (arg) {console.log (date.now ()) + "start callp3 avec arg =" + arg); return new promed (function (res, rej) {setTimeout (function () {res ("callp3");}, arg * 1000);});} callp1 (). puis (function () {console.log (date.now () + "callp1 return"); return callp2 ();}). puis (return) {console.log (date.now (). Value = "+ JSON.StRINGIFY (RET)); return callp3 (ret.arg1);}). puis (fonction (ret) {console.log (date.now () +" callp3 return with Ret Value = "+ ret);}) $ Node promest.js1450191479575 Démarrer Callp11450191481597 Callp1 return1450191481599 Démarrer Callp21450191484605 Callp2 Retour avec RET Valeur = {"Arg1": 4, "Arg2": "ARG2 Value"} 1450191484605 41450191488610 callp3 return with ret valeur = callp3L'expérience d'apprentissage simple ci-dessus de l'utilisation de promesses pour résoudre les appels asynchrones multicouches est tout le contenu que je partage avec vous. J'espère que vous pourrez vous faire référence et j'espère que vous pourrez soutenir Wulin.com plus.