
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 bitbyte bits são geralmente combinados como uma unidade.1 byte = 8 bit , 1kb = 1024 byte , 1M = 1024kb , 1 G = 1024 MTCPint 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-8const 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);
})
}) sharpconst 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, 8kbO 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çãomessagelog . 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
ajaxsetTimeoutde
setInterval DOM UI Renderingmicrotask 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