dados binários
Todo o conteúdo do computador: texto, números, imagens, áudio e vídeo serão eventualmente representados por binário.
JS
pode processar diretamente dados muito intuitivos: como strings Geralmente exibimos esse conteúdo aos usuários,
mas você pode pensar que JS It. também pode processar imagens.
JS
HTML
informar ao navegador o endereço da imagem.No entanto, é diferente para o servidor.
utf-8
, mas com GBK
. devemos ler seus dados binários e então convertê-los em texto correspondente por meio do GKB.sharp
no Node, que é responsável por ler as imagens ou Buffer
das imagens recebidas e depois processá-las.Node
, uma conexão longa é estabelecida através TCP
e transmite um byte. stream Precisamos Os dados são convertidos em bytes antes de serem transmitidos, e o tamanho dos bytes transmitidos precisa ser conhecido (o cliente precisa avaliar quanto conteúdo ler com base no tamanho)Buffer e Binário
Descobriremos isso. para o desenvolvimento front-end, geralmente raramente está relacionado ao tratamento binário entre si, mas para o lado do servidor, para implementar muitas funções, devemos operar diretamente seus dados binários
. , Node
nos fornece uma classe chamada Buffer
e é global. Como
dissemos antes, os dados binários são armazenados no Buffer, então como eles são armazenados?
8
bits: 00000000
, que é exatamente um byte.
1 byte = 8 bit
byte
bits são geralmente combinados como uma unidade.1 byte = 8 bit
, 1kb = 1024 byte
, 1M = 1024kb
, 1 G = 1024 M
TCP
int
em muitas linguagens de programação tem 4
bytes e o tipo long
tem 8
bytes.RGB
são 255
respectivamente, portanto, em essência,o Buffer e o
Buffer
de string são armazenados com um byte no computador, que é. equivalente a um array de bytes. Cada item do array tem um byte de tamanho.
Se quisermos colocar uma string em um buffer, qual é o processo?
buffer
.const message = 'Hello'. // Use a palavra-chave new para criar uma instância de buffer, mas este método de criação expirou const buffer = new Buffer(message) console.log(buffer); // <Buffer 48 65 6c 6c 6f> console.log(buffer.toString()); // Olá,
codificação e decodificação de string chinesa.
buffer
é utf-8
, portanto, no código a seguir, a classe Buffer
usa a codificação utf-8 para codificar nossa string. , também usamos utf-8 para decodificar nossas strings.3
bytesconst message = 'Hello'. // Use Buffer.from para decodificar nossa string const buffer = Buffer.from(message) console.log(buffer); // <Buffer e4 bd a0 e5 a5 bd e5 95 8a> // Existe um método toString na instância do buffer que pode decodificar a codificação console.log(buffer.toString()); // 'Hello'
, o que acontecerá se a codificação e a decodificação usarem formas diferentes de resultados de codificação?
const message = 'Hello' const buffer = Buffer.from(mensagem, 'utf16le') console.log(buffer); // <Buffer 60 4f 7d 59 4a 55> console.log(buffer.toString()); // `O}YJU
Outras maneiras de criar buffers
Existem muitas maneiras de criar buffer
. Aqui podemos criar Buffer
por meio de alloc
. cada um bit é modificado.
// que pode especificar nosso buffer. Por exemplo, se 8 for passado aqui, o buffer criado terá 8 elementos e o número binário correspondente a cada elemento será 0. buffer const = Buffer.alloc(8) console.log(buffer); // <Buffer 00 00 00 00 00 00 00 00> // Se o valor for atribuído a um número decimal, o buffer nos ajudará a convertê-lo em um número hexadecimal e então escrevê-lo no local correspondente buffer[0] = 88 // Em js, qualquer coisa que comece com 0x é representada como um número hexadecimal buffer[1] = 0x88 console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>
Operações de buffer e arquivo
1. Se o arquivo de texto
buffer
original será retornado diretamente , que é o resultado do conteúdo do utf-8
const fs = require('fs')do conteúdo do arquivo.
fs.readFile('./a.txt', (err, dados) => { console.log(dados); // <Buffer e5 93 88 e5 93 88> })
const fs = require('fs') // codificação indica a codificação de caracteres usada para decodificação, e o padrão da codificação é utf-8 fs.readFile('./a.txt', { codificação: 'utf-8' }, (err, dados) => { console.log(data); // Haha})
const fs = require('fs') // A codificação usa codificação de caracteres utf16le e a decodificação usa o formato utf-8. Deve ser que a decodificação não esteja correta. , dados) => { console.log(dados); // Erro }) // O código acima é semelhante ao código a seguir const msg = 'Haha' buffer const = Buffer.from(msg, 'utf-8') console.log(buffer.toString('utf16le')); //
2. O arquivo de imagem
copia a codificação da imagem para atingir o objetivo de copiar a imagem.
encoding
, pois a codificação de caracteres. só é lido ao ler a imagem. Só é útil ao buscar arquivos de texto. fs.readFile('./logo.png', (err, dados) => { console.log(data); // O que é impresso é a codificação binária correspondente ao arquivo de imagem // Também podemos escrever a codificação da imagem em outro arquivo, o que equivale a copiarmos a imagem fs.writeFile(' ./bar .png', dados, err => { console.log(erro); }) })
sharp
const sharp = require('sharp') // Corte a imagem logo.png para 200x300 e copie-a para o arquivo bax.png sharp('./logo.png') .resize(200, 300) .toFile('./bax.png', (err, informação) => { console.log(erro); }) // Você também pode primeiro converter o arquivo de imagem em um buffer e depois gravá-lo no arquivo. Você também pode copiar a imagem sharp('./logo.png') .resize(300, 300) .toBuffer() .então(dados => { fs.writeFile('./baa.png', dados, err => { console.log(erro); }) })
Processo de criação de buffer
Buffer
, não solicitaremos frequentemente a memória do sistema operacional. Por padrão, ele solicitará primeiro uma memória de 8 * 1024
bytes, ou seja, 8kb
O que é um loop de eventos?
Qual é o loop de eventos?
JS
que escrevemos e o navegador ou Node
.JS
que escrevemos e as chamadas de API do navegador ( setTimeout
, AJAX
,监听事件
, etc. ) As pontes se comunicam por meio de funções de retorno de chamada.file system
, networ
, etc.).Processo e thread
Processo e thread são dois conceitos no sistema operacional:
process
): o programa que o computador executouthread
): a menor unidade que o sistema operacional pode executar o cronograma de cálculo, para que CPU
possa operar diretamente o thread, queparece muito abstrato, vamos explicar intuitivamente:
Vamos usar um exemplo vívido para explicar
de desenvolvimento multiprocesso
. e verificação de informações) funcionam ao mesmo tempo?
CPU
é muito rápida e pode alternar rapidamente entre vários processos.Navegadores e JavaScript
Costumamos dizer que JavaScript
é de thread único, mas o thread JS deve ter seu próprio processo de contêiner Node
o navegador ou o navegador Node é um processo?
tab
, um novo processo será iniciado. Isso evita que uma página fique presa e faça com que todas as páginas parem de responderNo entanto, a execução do código JavaScript é executada em um thread separado.
JS
mesmo tempo.do processo de execução do JavaScript
não será executada até que seja colocada na pilha de chamadas de função. Vamos analisar o processo de execução do código
const message = 'Hello World'. console.log(mensagem); função soma(num1, num2) { retornar num1 + num2 } função foo() { resultado const = soma (20, 30) console.log(resultado); } foo()
main
como outras linguagens de programaçãomessage
log
. a função será colocada Entre na pilha de chamadas de função Após a execução, abra a pilhafoo
sum
é colocada na pilha de chamadas de função.js
é executado e a função principal é retirada doloop de eventos do navegador
. E se houver operações assíncronas durante a execução do código JS
?
setTimeout
no meio. (chamamos isso de função timer
), quando ela será executada?
web api
. O navegador armazenará a função de retorno de chamada antecipadamente. No momento apropriado, a função do temporizador será adicionada a uma fila de eventosPor que setTimeout não bloqueia a execução do código
É porque o navegador mantém uma coisa muito, muito importante - o
navegador do loop de eventos nos ajudará a salvar a função de retorno de chamada em setTimeout de alguma forma. O método mais comum é salvá-lo em uma árvore vermelha e preta
e esperar até que setTimeout seja agendado. Quando chegar a hora do cronômetro, nossa função de retorno de chamada do cronômetro será retirada do local salvo e colocada na fila de eventos.
Assim que o loop de eventos descobrir que há algo em nossa fila e a pilha de chamadas de função atual estiver vazia, outro. Depois que o código de sincronização for executado, as funções de retorno de chamada em nossa fila serão retiradas da fila e colocadas na pilha de chamadas de função para execução (a próxima função não será colocada na pilha até que a função anterior na fila seja retirada, é claro)
. , não há Deve haver apenas um evento. Por exemplo, durante um determinado processo, o usuário clica em um botão no navegador. Podemos ter um monitor para o clique desse botão, que corresponde a uma função de retorno de chamada. também será adicionado ao nosso Na fila, a ordem de execução é baseada na ordem em que estão na fila de eventos. Há também um resumo dos retornos de chamada que enviamos solicitações ajax
para a fila de eventos
: Na verdade, o loop de eventos é uma coisa muito simples. Isso significa que quando um determinado retorno de chamada precisa ser executado em uma situação especial, o retorno de chamada será salvo. antecipadamente é colocado na fila de eventos e o loop de eventos o retira e o coloca na pilha de chamadas de função.
Tarefas macro e micro tarefas
No entanto, o loop de eventos não mantém apenas umamacrotask queue
. Na verdade, existem duas filas, e a execução das tarefas na fila deve esperar
ajax
setTimeout
de
setInterval
DOM
UI Rendering
microtask queue
): then
de chamada Promise
, Mutation Observer API
, queueMicrotask()
, etc.main script
é executado primeiro (o código do script de nível superior escrito.
Pontos de teste: main stcipt
, setTimeout
, Promise
, then
, queueMicrotask
setTimeout(() => { console.log('conjunto1');4 nova promessa(resolver => { resolver() }).then(resolver => { nova promessa(resolver => { resolver() }).então(() => { console.log('então4'); }) console.log('então2'); }) }) nova promessa(resolver => { console.log('pr1'); resolver() }).então(() => { console.log('então1'); }) setTimeout(() => { console.log('set2'); }) console.log(2); filaMicrotask(() => { console.log('queueMicrotask'); }) nova promessa(resolver => { resolver() }).então(() => { console.log('então3'); }) //pr1 //2 //então1 //filaMicrotask //então3 //conjunto1 //então2 //então4 // set2
setTimeout
será colocado na pilha de chamadas de função imediatamente e será retirado da pilha imediatamente após a execução. Sua função timer
será colocada na fila de tarefas da macro.
A função passada para a classe Promise
será executada imediatamente. Não é uma função de retorno de chamada, então pr1
será impresso e, como o método resolve
é executado, o status da Promise mudará imediatamente para fulfilled
, de modo que quando a função then
for executada, sua função de retorno de chamada correspondente será. será colocada na fila de microtarefas e
uma função setTimeout será encontrada novamente. Quando a pilha for exibida, sua função de timer será colocada na fila de tarefas de macro
2
encontrar console.log
. é impresso e, em seguida, exibido.
Uma função é vinculada queueMicrotask
aqui e a função será colocada. Depois de entrar na fila de microtarefas,
uma nova instrução Promise foi encontrada, mas imediatamente alterou o status da promessa para cumprida, portanto, o retorno de chamada. correspondente à função then também foi colocada na fila de microtarefas.
Como o código do script de sincronização foi executado, agora o evento No início do loop, as tarefas que competem com a fila de microtarefas e a macrotarefa são colocadas no. pilha de chamadas de função em ordem de prioridade Nota: A prioridade das microtarefas é maior que a das macrotarefas. Você deve lê-la sempre antes de executar uma macrotarefa. não está vazio, você precisa executar primeiro a tarefa da fila de microtarefas.
A primeira microtarefa é imprimir then1
, a segunda microtarefa é imprimir queueMicrotask e a terceira microtarefa é imprimir then3
. comece a executar a tarefa de macro.
A primeira tarefa de macro é mais complicada. Ela primeiro imprimirá set1
e, em seguida, executará uma new promise
que mudará imediatamente o estado. A fila não está vazia, portanto, uma fila de microtarefas com prioridade mais alta precisa ser executada, o que equivale ao retorno de chamada então sendo executado imediatamente. É a mesma nova instrução Promise, e sua troca correspondente é colocada na fila de microtarefas. Observe que existe uma função console
após a nova instrução Promise. Esta função será executada imediatamente após a execução da nova instrução Promise, ou seja, imprimindo then2
. then4
. Até agora, a fila de microtarefas está vazia e a fila de macrotarefas pode continuar a ser executada
, então o próximo set2
de macrotarefas2 será impresso. Após a execução da macrotarefa,
o resultado da impressão de todo o código é: pr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2
Perguntas da entrevista <2>
Pontos de teste: main script
, setTimeout
, Promise
, then
, queueMicrotask
, await
, async
suplemento de conhecimento: async, await é um açúcar de sintaxe para Promise
. Ao lidar com problemas de loop de eventos,
new Promise((resolve,rejcet) => { 函数执行})
then(res => {函数执行})
na promessa anteriorfunção assíncrona async1() { console.log('início assíncrono1'); aguarde async2() console.log('fim async1'); } função assíncrona async2() { console.log('async2'); } console.log('inicialização do script'); setTimeout(() => { console.log('setTimeout'); }, 0) assíncrono1() nova promessa(resolver => { console.log('promessa1'); resolver() }).então(() => { console.log('promessa2'); }) console.log('fim do script'); // início do script //início assíncrono1 // assíncrono2 // promessa1 //fim do script // fim assíncrono1 // promessa2 // setTimeout
é uma definição de função no início e não precisa ser colocado na pilha de chamadas de função para execução até encontrar a primeira instrução console
. Depois de enviar a pilha, execute o script start
e, em seguida, retire-o da pilha. pilha
para encontrar a primeira função setTimeout
, que corresponde a timer
será colocado na fila de tarefas de macro
e a função async1 será executada Primeiro, async1 start
será impresso e, em seguida, a função async2
após await
será executada. Como mencionado anteriormente, a função após a palavra-chave await é considerada new Promise
, esta função será executada imediatamente, então async2 será impressa, mas o código após a instrução await é equivalente a ser colocado no then. retorno de chamada, ou seja, console.log('async1 end')
Esta linha de código é colocada na fila de microtarefas
e o código continua a ser executado. Ele encontra uma nova instrução Promise, então a função promise1
é imediatamente impressa. em seguida, o retorno de chamada é colocado na fila de microtarefas para
executar
a última função do console para impressão. O código script end
sincronização foi executado. O loop de eventos irá para as filas de tarefas macro e de microtarefas para executar tarefas.
fila de tarefas. A instrução de impressão correspondente à primeira microtarefa será executada, o que significa que async1 end
será impresso e, em seguida, promise2
será impressa. Neste momento, a fila de microtarefas está vazia e as tarefas na fila de macrotarefas são iniciadas. será executado.
O setTimeout correspondente à função do timer será impresso. Neste momento, a macrotarefa também é executada, e a sequência de impressão final é: script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout