données binaires
Tout le contenu de l'ordinateur : le texte, les chiffres, les images, l'audio et la vidéo seront éventuellement représentés sous forme binaire.
JS
peut traiter directement des données très intuitives : telles que des chaînes. Nous affichons généralement ces contenus aux utilisateurs,
mais vous pourriez penser que JS le fait. peut également traiter des images.
JS
ou HTML
vers le navigateur. Il se charge uniquement de communiquer au navigateur l'adresse de l'image.Cependant, c'est différent pour le serveur.
utf-8
, mais avec GBK
, puis. nous devons lire leurs données binaires. Les données binaires sont ensuite converties en texte correspondant via GKB.sharp
dans Node, qui est responsable de la lecture des images ou Buffer
des images entrantes puis de leur traitement.Node
, une longue connexion est établie via TCP
et transmet un octet. Nous devons Les données sont converties en octets avant d'être transmises, et la taille des octets transmis doit être connue (le client doit juger de la quantité de contenu à lire en fonction de la taille)Tampon et binaire
Nous constaterons que pour le développement front-end, il est généralement rarement lié aux relations binaires les uns avec les autres, mais pour le côté serveur, afin d'implémenter de nombreuses fonctions, nous devons
donc exploiter directement ses données binaires, afin de permettre aux développeurs de remplir plus de fonctions. , Node
nous fournit une classe nommée Buffer
, et elle est globale Comme
nous l'avons dit précédemment, les données binaires sont stockées dans le Buffer, alors comment sont-elles stockées ?
8
bits : 00000000
, ce qui correspond exactement à un octet. Pourquoi est-il de 8 bits ?
1 byte = 8 bit
byte
.1 byte = 8 bit
, 1kb = 1024 byte
, 1M = 1024kb
, 1 G = 1024 M
int
dans de nombreux langages de programmation est 4
octets et le type long
est de 8
octets.TCP
transmetRGB
sont respectivement 255
, donc en substance,le Buffer et la chaîne
Buffer
sont stockés avec un octet dans l'ordinateur, ce qui est. équivalent à un tableau d'octets. Chaque élément du tableau a une taille d'un octet.
Si nous voulons mettre une chaîne dans un tampon, quel est le processus ?
buffer
.const message = 'Bonjour'. // Utilisez le mot-clé new pour créer une instance de tampon, mais cette méthode de création a expiré. const buffer = new Buffer(message) console.log(tampon); // <Tampon 48 65 6c 6c 6f> console.log(buffer.toString()); // Bonjour
l'encodage et le décodage de chaîne chinoise.
buffer
est utf-8
, donc dans le code suivant, la classe Buffer
utilise l'encodage utf-8 pour encoder notre chaîne. , nous utilisons également utf-8 pour décoder nos chaînes.3
message const d'encodage binaire sur 3 octets = 'Bonjour'. // Utilisez Buffer.from pour décoder notre chaîne const buffer = Buffer.from(message) console.log(buffer); // <Tampon e4 bd a0 e5 a5 bd e5 95 8a> // Il existe une méthode toString dans l'instance buffer qui peut décoder l'encodage console.log(buffer.toString()); // 'Bonjour'
, que se passera-t-il si l'encodage et le décodage utilisent différentes formes de résultats d'encodage ?
const message = 'Bonjour' const buffer = Buffer.from(message, 'utf16le') console.log(tampon); // <Tampon 60 4f 7d 59 4a 55> console.log(buffer.toString()); // `O}YJU
Autres façons de créer Buffers
Il existe de nombreuses façons de créer buffer
. Ici, nous pouvons créer Buffer
via alloc
Nous pouvons créer directement des instances de tampon sous forme de tableau pour
Chaque bit est modifié.
// qui peut spécifier notre nombre de chiffres. . Par exemple, si 8 est transmis ici, alors le tampon créé aura 8 éléments et le nombre binaire correspondant à chaque élément est 0. tampon const = Buffer.alloc(8) console.log(tampon); // <Tampon 00 00 00 00 00 00 00 00> // Si la valeur est affectée à un nombre décimal, le tampon nous aidera à la convertir en nombre hexadécimal puis à l'écrire dans l'emplacement correspondant buffer[0] = 88 // En js, tout ce qui commence par 0x est représenté comme un nombre hexadécimal buffer[1] = 0x88 console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>
Opérations sur le tampon et les fichiers
1. Si le fichier texte
buffer
d'origine sera renvoyé directement , qui est le résultat du contenu du fichier. Nombre binaire codé en utf-8
const fs = require('fs') fs.readFile('./a.txt', (erreur, données) => { console.log(data); // <Tampon e5 93 88 e5 93 88> })
const fs = require('fs') // l'encodage indique l'encodage des caractères utilisé pour le décodage, et l'encodage par défaut est utf-8 fs.readFile('./a.txt', { encodage : 'utf-8' }, (err, data) => { console.log(data); // Haha})
const fs = require('fs') // L'encodage utilise l'encodage de caractères utf16le et le décodage utilise le format utf-8. Il doit être que le décodage n'est pas correct. fs.readFile('./a.txt', { encoding: 'utf16le' }, (err. , données) => { console.log(données); // Erreur }) // Le code ci-dessus est similaire au code suivant const msg = 'Haha' const buffer = Buffer.from(msg, 'utf-8') console.log(buffer.toString('utf16le')); //
2. Le fichier image
copie l'encodage de l'image pour atteindre l'objectif de copie de l'image
encoding
, car l'encodage des caractères. n'est lu que lors de la lecture de l'image. Il n'est utile que lors de la récupération de fichiers texteconst fs = require('fs') fs.readFile('./logo.png', (erreur, données) => { console.log(data); // Ce qui est imprimé est l'encodage binaire correspondant au fichier image // On peut également écrire l'encodage de l'image dans un autre fichier, ce qui équivaut à copier l'image fs.writeFile(' ./bar .png', données, erreur => { console.log(err); }) })
sharp
const Sharp = require('sharp') // Recadre l'image logo.png à 200x300 et copie-la dans le fichier bax.png sharp('./logo.png') .resize(200, 300) .toFile('./bax.png', (erreur, info) => { console.log(err); }) // Vous pouvez également d'abord convertir le fichier image en tampon, puis l'écrire dans le fichier. Vous pouvez également copier l'image Sharp('./logo.png') .resize(300, 300) .toBuffer() .then(données => { fs.writeFile('./baa.png', data, err => { console.log(err); }) })
Processus de création de tampon
Buffer
, nous ne demanderons pas fréquemment de mémoire au système d'exploitation. Par défaut, il demandera d'abord une mémoire de 8 * 1024
octets, soit 8kb
Qu'est-ce qu'une boucle d'événements ?
Qu'est-ce que la boucle d'événements ?
JS
que nous écrivons et le navigateur ou Node
.JS
que nous écrivons et les appels d'API du navigateur ( setTimeout
, AJAX
,监听事件
, etc. ) Les ponts communiquent via des fonctions de rappel.file system
, networ
, etc.). Les ponts communiquent également via des fonctions de rappel.Processus et thread
Processus et thread sont deux concepts dans le système d'exploitation :
process
) : le programme que l'ordinateur a exécutéthread
) : la plus petite unité sur laquelle le système d'exploitation peut exécuter le calendrier de calcul, afin que CPU
puisse fonctionner directement le thread, quisemble très abstrait. , expliquons-le intuitivement :
Utilisons un exemple frappant pour expliquer
de développement multi-processus et multi-threads
(tout en écoutant de la musique, en écrivant du code). , et vérification des informations) fonctionnent en même temps ?
CPU
est très rapide et il peut basculer rapidement entre plusieurs processus.Navigateurs et JavaScript
On dit souvent que JavaScript
est monothread, mais le thread JS doit avoir son propre processus conteneur Node
le navigateur ou le navigateur Node est-il un processus ?
tab
, un nouveau processus est lancé afin d'éviter qu'une page ne reste bloquée et que toutes les pages ne répondent plusCependant, l'exécution du code JavaScript est exécutée dans un thread séparé,
JS
ne peut faire qu'une seule chose à la foisdu processus d'exécution JavaScript
ne sera pas exécutée tant qu'elle ne sera pas insérée dans la pile d'appels de fonction. Analysons le processus d'exécution du code
const message = 'Hello World'. console.log(message); fonction somme (num1, num2) { retourner num1 + num2 } fonction foo() { résultat const = somme (20, 30) console.log(résultat); } foo()
main
comme d'autres langages de programmationmessage
variablelog
. La fonction sera placée Entrez dans la pile d'appel de fonction.Après l'exécution, ouvrez la pilefoo
fonction foo est poussée dans la pile d'appel de fonction.Cependant, la fonction sum
doit être appelée pendant l'exécution,js
est exécuté et la fonction principale est sortie dela boucle d'événements du navigateur
. Et s'il y avait des opérations asynchrones pendant l'exécution du code JS
?
setTimeout
au milieuEnsuite, la fonction est passée dans la fonction setTimeout. (nous l'appelons fonction timer
), quand sera-t-elle exécutée ?
web api
. Le navigateur stockera la fonction de rappel à l'avance, au moment approprié, la fonction timer sera ajoutée à une file d'attente d'événements.Pourquoi setTimeout ne bloque-t-il pas l'exécution du code
C'est parce que le navigateur maintient une chose très, très importante : le
navigateur de boucles d'événements nous aidera à enregistrer la fonction de rappel dans setTimeout d'une manière ou d'une autre. La méthode la plus courante consiste à l'enregistrer dans une arborescence rouge-noir
et à attendre que setTimeout soit planifié. Lorsque l'heure de la minuterie arrive, notre fonction de rappel de minuterie sera retirée de l'emplacement enregistré et placée dans la file d'attente des événements.
Une fois que la boucle d'événements aura détecté qu'il y a quelque chose dans notre file d'attente et que la pile d'appels de fonction actuelle est vide, autre. Une fois le code de synchronisation exécuté, les fonctions de rappel de notre file d'attente seront retirées de la file d'attente et placées dans la pile d'appels de fonction pour exécution (la fonction suivante ne sera pas poussée dans la pile tant que la fonction précédente de la file d'attente ne sera pas sortie,
bien sûr
)., il n'y a pas Il ne doit y avoir qu'un seul événement. Par exemple, lors d'un certain processus, l'utilisateur clique sur un bouton dans le navigateur. Nous pouvons avoir un moniteur pour le clic de ce bouton, ce qui correspond à une fonction de rappel. seront également ajoutés à notre Dans la file d'attente, l'ordre d'exécution est basé sur l'ordre dans lequel ils se trouvent dans la file d'attente des événements. Il y a aussi un résumé des rappels que nous envoyons aux requêtes ajax
à la file d'attente des événements
: En fait, la boucle d'événements est une chose très simple, cela signifie que lorsqu'un certain rappel doit être exécuté dans une situation particulière, il sera enregistré. à l'avance est placé dans la file d'attente des événements, et la boucle d'événements le retire et le place dans la pile d'appels de fonction.
Macrotâches et microtâches.
Cependant, la boucle d'événements ne maintient pas une seule file d'attente. En fait, il y a deux files d'attente, et l'exécution des tâches dans la file d'attente doit attendre
macrotask queue
ajax
setTimeout
, setInterval
, surveillance DOM
, UI Rendering
et autresmicrotask queue
): Promise
's then
callback, Mutation Observer API
, queueMicrotask()
, etc.Alors, quelle est la priorité des deux files d'attente dans la boucle d'événements ?
main script
est exécuté en premier (le code du script de niveau supérieur écrit).dire
Points de test : main stcipt
, setTimeout
, Promise
, then
, queueMicrotask
setTimeout(() => { console.log('set1');4 nouvelle promesse (résolution => { résoudre() }).then(resolve => { nouvelle promesse (résolution => { résoudre() }).then(() => { console.log('then4'); }) console.log('then2'); }) }) nouvelle promesse (résolution => { console.log('pr1'); résoudre() }).then(() => { console.log('then1'); }) setTimeout(() => { console.log('set2'); }) console.log(2); queueMicrotask(() => { console.log('queueMicrotask'); }) nouvelle promesse (résolution => { résoudre() }).then(() => { console.log('then3'); }) // pr1 // 2 //puis1 //file d'attenteMicrotâche //puis3 //set1 //puis2 //puis4 // set2
setTimeout
sera immédiatement placé dans la pile d'appels de fonction et sera retiré de la pile immédiatement après l'exécution. Sa fonction timer
sera placée dans la file d'attente des tâches de macro.
La fonction passée dans la classe Promise
sera exécutée immédiatement. Ce n'est pas une fonction de rappel, donc elle sera imprimée pr1
, et comme la méthode resolve
est exécutée, le statut de la promesse passera immédiatement à fulfilled
, de sorte que lorsque la fonction then
sera exécutée, sa fonction de rappel correspondante le sera. être placé dans la file d'attente des microtâches et
une fonction setTimeout est à nouveau rencontrée. Lorsque la pile est sautée, sa fonction de minuterie sera placée dans la file d'attente des tâches de macro
lorsqu'elle rencontrera console.log
. Une fois la fonction placée sur la pile, 2
est imprimé, puis affiché.
Une fonction est liée à queueMicrotask
ici, et la fonction sera placée. Après être entré dans la file d'attente des microtâches,
une nouvelle instruction Promise a été rencontrée, mais elle a immédiatement changé le statut de la promesse en rempli, donc le rappel correspondant à la fonction then a également été mis dans la file d'attente des micro-tâches.
Puisque le code du script de synchronisation a été exécuté, maintenant l'événement Au début de la boucle, les tâches en compétition avec la file d'attente des micro-tâches et la macro-tâche sont mises dans la file d'attente des micro-tâches. pile d'appels de fonctions par ordre de priorité. Remarque : La priorité des micro-tâches est supérieure à celle des macro-tâches. Vous devez la lire à chaque fois avant de vouloir exécuter une macro-tâche. Vérifiez si la file d'attente des micro-tâches est vide. il n'est pas vide, vous devez d'abord exécuter la tâche de la file d'attente des microtâches.
La première microtâche consiste à imprimer then1
, la deuxième microtâche consiste à imprimer queueMicrotask et la troisième microtâche consiste à imprimer then3
. commencez à exécuter la tâche macro.
La première tâche macro est plus compliquée. Elle imprimera d'abord set1
, puis exécutera une new promise
qui changera immédiatement l'état. Son rappel sera alors placé dans la file d'attente des microtâches. La file d'attente n'est pas vide, donc une file d'attente de microtâches avec une priorité plus élevée doit être exécutée, ce qui équivaut à l'exécution immédiate du rappel then. Il s'agit de la même nouvelle instruction Promise, et son swap correspondant est placé dans la file d'attente des microtâches. Notez qu'il y a une fonction console
après la nouvelle instruction Promise. Cette fonction sera exécutée immédiatement après l'exécution de la nouvelle instruction Promise, c'est-à-dire l'impression then2
. Il y a encore une tâche dans la confrontation des microtâches, l'étape suivante consiste donc à imprimer. then4
. Jusqu'à présent, la file d'attente des microtâches est vide et la file d'attente des macrotâches peut continuer à être exécutée
, donc la prochaine macrotâche set2
sera imprimée une fois la macrotâche exécutée,
le résultat de l'impression de l'intégralité du code est : pr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2
Questions d'entretien <2>
Points de test : main script
, setTimeout
, Promise
, then
, queueMicrotask
, await
async
supplément de connaissances asynchrone : async, wait est un sucre de syntaxe pour Promise
. Lorsque nous traitons de problèmes de boucle d'événements,
new Promise((resolve,rejcet) => { 函数执行})
then(res => {函数执行})
dans lafonction asynchrone Promise précédente async1() { console.log('démarrage async1'); attendre async2() console.log('fin async1'); } fonction asynchrone async2() { console.log('async2'); } console.log('démarrage du script'); setTimeout(() => { console.log('setTimeout'); }, 0) async1() nouvelle promesse (résolution => { console.log('promesse1'); résoudre() }).then(() => { console.log('promise2'); }) console.log('fin du script'); // démarrage du script // démarrage async1 // async2 // promesse1 // fin du script // fin async1 // promesse2 // setTimeout
est une définition de fonction au début et n'a pas besoin d'être poussé dans la pile d'appels de fonction pour être exécuté jusqu'à ce qu'il rencontre la première instruction console
. Après avoir poussé la pile, exécutez le script start
d'impression, puis sortez-le de la. pile
pour rencontrer la première fonction setTimeout
, qui correspond à timer
sera placé dans la file d'attente des tâches de macro
et la fonction async1 sera exécutée. Tout d'abord, async1 start
sera imprimé, puis la fonction async2
sera exécutée après await
. Parce que comme mentionné précédemment, la fonction après le mot-clé wait est considérée comme new Promise
, cette fonction sera exécutée immédiatement, donc async2 sera imprimée, mais le code après l'instruction wait équivaut à être placé dans then. rappel, c'est-à-dire console.log('async1 end')
Cette ligne de code est placée dans la file d'attente des microtâches
et le code continue de s'exécuter. Il rencontre une nouvelle instruction Promise, donc promise1
est immédiatement imprimée. Ensuite, le rappel est placé dans la file d'attente des microtâches pour
Fin
script end
, la boucle d'événements ira à la tâche macro et aux files d'attente des microtâches pour exécuter les tâches.
file d'attente des tâches. L'instruction d'impression correspondant à la première micro-tâche sera exécutée, ce qui signifie que async1 end
sera imprimée, puis promise2
sera imprimée. À ce moment, la file d'attente des microtâches est vide et les tâches de la file d'attente des macrotâches commencent. être exécuté.
Le setTimeout correspondant à la fonction timer sera imprimé. À ce moment, la macrotâche est également exécutée et la séquence d'impression finale est la suivante : script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout