L'asynchrone consiste à augmenter le taux d'occupation du processeur et à le maintenir occupé tout le temps.
Certaines opérations (la plus typique est les E/S) ne nécessitent pas la participation du processeur et prennent beaucoup de temps. Si l'asynchrone n'est pas utilisé, cela formera un état de blocage, provoquant l'inactivité du processeur et le gel de la page.
Lorsqu'une opération d'E/S se produit dans un environnement asynchrone, le CPU met le travail d'E/S de côté (à ce moment, les E/S sont prises en charge par d'autres contrôleurs et les données sont toujours en cours de transmission), puis traite la tâche suivante. , en attendant que l'opération d'E/S soit terminée, avertissez le CPU (le rappel est une méthode de notification) de revenir au travail.
Le contenu principal de "JavaScript Asynchronous and Callback" est que l'heure de fin spécifique du travail asynchrone est incertaine. Afin d'effectuer avec précision le traitement ultérieur une fois le travail asynchrone terminé, un rappel doit être transmis à la fonction asynchrone, de sorte qu'Après. terminer votre travail, continuez avec les tâches suivantes.
Bien que les rappels puissent être très simples à implémenter de manière asynchrone, ils peuvent former un enfer de rappel en raison de l’imbrication multiple. Pour éviter l'enfer des rappels, vous devez dénicher et modifier la programmation imbriquée en programmation linéaire.
Promise
est la meilleure solution pour gérer l'enfer des rappels en JavaScript
.
Promise
être traduit par « promesse ». Nous pouvons résumer le travail asynchrone et l'appeler une Promise
, c'est-à-dire faire une promesse et promettre de donner un signal clair une fois le travail asynchrone terminé !
Syntaxe Promise
:
let promise = new Promise(function(resolve,reject){ // Travail asynchrone})
Grâce à la syntaxe ci-dessus, nous pouvons encapsuler le travail asynchrone dans une Promise
. La fonction transmise lors de la création Promise
est la méthode de gestion du travail asynchrone, également appelée executor
.
resolve
et reject
sont des fonctions de rappel fournies par JavaScript
lui-même. Elles peuvent être appelées lorsque executor
termine la tâche :
resolve(result)
- si elle est terminée avec succès, result
sera renvoyéreject(error)
- si l'exécution échoue et error
d'erreur est renvoyé. error
sera générée ;executor
s'exécutera automatiquement immédiatement après la création Promise
, et son statut d'exécution modifiera l'état des propriétés internes de Promise
:
state
- initialement pending
, puis converti en fulfilled
après l'appel de resolve
ou sera rejected
lorsque reject
est appelé ;result
——Il undefined
au départ, puis devient value
après l'appel resolve(value)
, ou devient error
après l'appel reject
fs.readFile
une fonction asynchrone ; .Nous pouvons le transmettre à executor
Les opérations de lecture de fichiers sont effectuées dans le fichier, encapsulant ainsi le travail asynchrone.
Le code suivant encapsule la fonction fs.readFile
et utilise resolve(data)
pour gérer les résultats réussis et reject(err)
pour gérer les résultats échoués.
Le code est le suivant :
let promise = new Promise((resolve, rejet) => { fs.readFile('1.txt', (erreur, données) => { console.log('Lire 1.txt') si (erreur) rejeter (erreur) résoudre (données) })})
Si nous exécutons ce code, les mots "Read 1.txt" seront affichés, prouvant que l'opération de lecture du fichier est effectuée immédiatement après la création Promise
.
Promise
encapsule généralement le code asynchrone en interne, mais elle n'encapsule pas uniquement le code asynchrone.
Le cas Promise
ci-dessus encapsule l'opération de lecture de fichier. Le fichier sera lu immédiatement une fois la création terminée. Si vous souhaitez obtenir le résultat de l'exécution Promise
, vous devez utiliser trois méthodes then
, catch
et finally
.
La méthode then
de Promise
peut être utilisée pour gérer le travail une fois Promise
terminée. Elle reçoit deux paramètres de rappel. La syntaxe est la suivante :
promise.then(function(result),function(error))
result
est la valeur reçue par resolve
;error
est le paramètre reçu par reject
par exemple :
let;
promise = new Promise((résoudre, rejeter) => { fs.readFile('1.txt', (erreur, données) => { console.log('Lire 1.txt') si (erreur) rejeter (erreur) résoudre (données) })})promesse.puis( (données) => { console.log('Exécuté avec succès, le résultat est' + data.toString()) }, (erreur) => { console.log('L'exécution a échoué, l'erreur est' + err.message) })
Si la lecture du fichier est exécutée avec succès, la première fonction sera appelée :
PS E:CodeNodedemos 3-callback> node .index.js Lire 1.txt Si l'exécution réussit, le résultat est 1
supprimé 1.txt
. Si l'exécution échoue, la deuxième fonction sera appelée :
PS E:CodeNodedemos 3-callback> node .index.js. Lire 1.txt L'exécution a échoué avec l'erreur ENOENT : aucun fichier ou répertoire de ce type, ouvrez 'E:CodeNodedemos 3-callback1.txt'
Si nous nous concentrons uniquement sur le résultat d'une exécution réussie, nous ne pouvons transmettre qu'un seul fonction de rappel :
promise .then((data)=>{ console.log('Exécuté avec succès, le résultat est' + data.toString())})
À ce stade, nous avons implémenté une opération de lecture asynchrone du fichier.
Si nous nous concentrons uniquement sur le résultat de l'échec, nous pouvons passer null
au premier then
rappeler : promise.then(null,(err)=>{...})
.
Ou utilisez une manière plus élégante : promise.catch((err)=>{...})
let promise = new Promise((resolve, rejet) => { fs.readFile('1.txt', (erreur, données) => { console.log('Lire 1.txt') si (erreur) rejeter (erreur) résoudre (données) })})promise.catch((err)=>{ console.log(err.message)})
.catch((err)=>{...})
et then(null,(err)=>{...})
ont exactement le même effet.
.finally
est une fonction qui sera exécutée quel que soit le résultat de promise
. Elle a le même objectif que finally
dans la syntaxe try...catch...
et peut gérer des opérations sans rapport avec le résultat.
Par exemple :
new Promise((resolve,reject)=>{ //quelque chose...}).finally(()=>{console.log('Execute indépendamment du résultat')}).then(result=>{...}, err=>{...} )Le rappel final n'a aucun paramètre
fs.readFile()
lit 10 fichiers séquentiellement et affiche le contenu. des dix fichiers de manière séquentielle.
Puisque fs.readFile()
lui-même est asynchrone, nous devons utiliser l'imbrication de rappel. Le code est le suivant :
fs.readFile('1.txt', (err, data) => { console.log(data.toString()) //1 fs.readFile('2.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('3.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('4.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('5.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('6.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('7.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('8.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('9.txt', (erreur, données) => { console.log(data.toString()) fs.readFile('10.txt', (erreur, données) => { console.log(data.toString()) // ==> La Porte de l'Enfer}) }) }) }) }) }) }) }) })})
Bien que le code ci-dessus puisse accomplir la tâche, à mesure que l'imbrication des appels augmente, le niveau de code devient plus profond et la difficulté de maintenance augmente, en particulier lorsque nous utilisons du code réel qui peut contenir de nombreuses boucles et instructions conditionnelles. simple console.log(...)
dans l'exemple.
Si nous n'utilisons pas de rappels et appelons directement fs.readFile()
dans l'ordre selon le code suivant, que se passera-t-il ?
//Remarque : ce n'est pas la bonne façon d'écrire fs.readFile('1.txt', (err, data) => { console.log(data.toString())})fs.readFile('2.txt', (err, data) => { console.log(data.toString())})fs.readFile('3.txt', (err, data) => { console.log(data.toString())})fs.readFile('4.txt', (err, data) => { console.log(data.toString())})fs.readFile('5.txt', (err, data) => { console.log(data.toString())})fs.readFile('6.txt', (err, data) => { console.log(data.toString())})fs.readFile('7.txt', (err, data) => { console.log(data.toString())})fs.readFile('8.txt', (err, data) => { console.log(data.toString())})fs.readFile('9.txt', (err, data) => { console.log(data.toString())})fs.readFile('10.txt', (err, data) => { console.log(data.toString())})
Voici les résultats de mon test (les résultats de chaque exécution sont différents) :
PS E:CodeNodedemos 3-callback> node .index.La raison pour laquelle
js12346957108
produit ce résultat non séquentiel est asynchrone , et le parallélisme multithread ne peut pas être obtenu dans un seul thread.
La raison pour laquelle ce cas d'erreur est utilisé ici est pour souligner le concept d'asynchronisme. Si vous ne comprenez pas pourquoi ce résultat se produit, vous devez revenir en arrière et rattraper la leçon !
L'idée d'utiliser Promise
pour résoudre la lecture séquentielle asynchrone de fichiers :
promise1
et utiliser resolve
pour renvoyer le résultat.promise1.then
pour recevoir et afficher le résultat de la lecture du fichier.promise2
Moyenne promise1.then
.Et revenirpromise2.then
pour recevoir et sortir le résultat de la lecturepromise3
Créer un nouvel objet promise2.then
et revenir pourpromise3.then
pour recevoir et sortir le résultat de la... le code est le suivant :
let promise1 = new Promise( (résoudre, rejeter) => { fs.readFile('1.txt', (erreur, données) => { si (erreur) rejeter (erreur) résoudre (données) })})laissez promise2 = promise1.then( données => { console.log(data.toString()) renvoyer une nouvelle promesse ((résoudre, rejeter) => { fs.readFile('2.txt', (erreur, données) => { si (erreur) rejeter (erreur) résoudre (données) }) }) })laissez promise3 = promise2.then( données => { console.log(data.toString()) renvoyer une nouvelle promesse ((résoudre, rejeter) => { fs.readFile('3.txt', (erreur, données) => { si (erreur) rejeter (erreur) résoudre (données) }) }) })laissez promise4 = promise3.then( données => { console.log(data.toString()) //..... })... ...
De cette façon, nous écrivons l'enfer de rappel imbriqué d'origine dans un mode linéaire.
Mais il y a toujours un problème avec le code. Bien que le code soit devenu plus beau en termes de gestion, cela augmente considérablement la longueur du code.
Le code ci-dessus est trop long. Nous pouvons réduire la quantité de code en deux étapes :
promise
intermédiaire et lier le .then
codez comme suit :
function myReadFile (chemin) { renvoyer une nouvelle promesse ((résoudre, rejeter) => { fs.readFile(chemin, (erreur, données) => { si (erreur) rejeter (erreur) console.log(data.toString()) résoudre() }) })}monFichierLire('1.txt') .then(data => { return myReadFile('2.txt') }) .then(data => { return myReadFile('3.txt') }) .then(data => { return myReadFile('4.txt') }) .then(data => { return myReadFile('5.txt') }) .then(data => { return myReadFile('6.txt') }) .then(data => { return myReadFile('7.txt') }) .then(data => { return myReadFile('8.txt') }) .then(data => { return myReadFile('9.txt') }) .then(data => { return myReadFile('10.txt') })
Puisque la méthode myReadFile
renverra une nouvelle Promise
, nous pouvons exécuter directement la méthode .then
Cette méthode de programmation est appelée programmation en chaîne .
Le résultat de l'exécution du code est le suivant :
PS E:CodeNodedemos 3-callback> node .index.js12345678910
Ceci termine l'opération de lecture asynchrone et séquentielle du fichier.
Remarque : Un nouvel objet
Promise
doit être renvoyé dans la méthode.then
de chaque étape, sinon l'anciennePromise
précédente sera reçue.En effet, chaque méthode
then
continuera à transmettre saPromise
vers le bas.