двоичные данные
Весь контент на компьютере: текст, числа, изображения, аудио и видео в конечном итоге будет представлен в двоичном виде.
JS
может напрямую обрабатывать очень интуитивные данные: например, строки. Обычно мы показываем это содержимое пользователям,
но вы можете подумать, что это JS. Также может обрабатывать изображения.
JS
или HTML
. Он отвечает только за сообщение браузеру адреса изображения.Однако для сервера все по-другому.
utf-8
, а с помощью GBK
. мы должны прочитать их. Двоичные данные затем преобразуются в соответствующий текст через GKB.sharp
, которая отвечает за чтение картинок или Buffer
входящих картинок и их последующую обработку.Node
устанавливается длинное соединение через TCP
, передающее байт. поток Нам нужно Данные преобразуются в байты перед передачей, и размер передаваемых байтов должен быть известен (клиент должен судить, какой объем контента нужно прочитать, исходя из размера).Буферный и двоичный.
Мы обнаружим это. для внешней разработки это обычно редко связано с двоичным взаимодействием друг с другом, но для серверной стороны, чтобы реализовать множество функций, мы должны напрямую управлять его двоичными данными.
Поэтому, чтобы облегчить разработчикам выполнение большего количества функций
Node
предоставляет нам класс с именем Buffer
, и он является глобальным. Как
мы уже говорили, двоичные данные хранятся в Buffer, так как же они хранятся?
8
-битный двоичный код: 00000000
, что составляет ровно один байт. Почему он 8-битный?
byte
1 byte = 8 bit
1 byte = 8 bit
, 1kb = 1024 byte
, 1M = 1024kb
, 1 G = 1024 M
int
во многих языках программирования составляет 4
байта, а тип long
— 8
байт.TCP
передаетRGB
равны 255
соответственно, поэтому по сутиBuffer и строковый
Buffer
хранятся в компьютере одним байтом, что составляет эквивалент массива байтов. Каждый элемент массива имеет размер одного байта.
Если мы хотим поместить строку в буфер, каков процесс?
buffer
.const message = 'Hello'. // Используйте ключевое слово new для создания экземпляра буфера, но срок действия этого метода создания истек const buffer = new Buffer(message) console.log(buffer); // <Буфер 48 65 6c 6c 6f> console.log(buffer.toString()); // Привет,
кодировка и декодирование строк на китайском языке.
buffer
по умолчанию — utf-8
, поэтому в следующем коде класс Buffer
использует кодировку utf-8 для кодирования нашей строки. мы также используем utf-8 для декодирования наших строк.3
байтовому двоичному сообщениюconst = «Hello». // Используем Buffer.from для декодирования нашей строки const buffer = Buffer.from(message) console.log(buffer); // <Буфер e4 bd a0 e5 a5 bd e5 95 8a> // В экземпляре буфера есть метод toString, который может декодировать кодировку console.log(buffer.toString()); // 'Здравствуйте'
, что произойдет, если кодирование и декодирование используют разные формы результатов кодирования?
const message = 'Hello' const buffer = Buffer.from(сообщение, 'utf16le') console.log(buffer); // <Буфер 60 4f 7d 59 4a 55> console.log(buffer.toString()); // `O}YJU
Другие способы создания буферов
buffer
Здесь мы можем создавать Buffer
с помощью alloc
Мы можем напрямую создавать экземпляры буферов в виде массивов. каждый бит изменяется.
// которое может указать количество цифр в нашем буфере. Например, если здесь передано 8, то созданный буфер будет содержать 8 элементов, а двоичное число, соответствующее каждому элементу, будет равно 0. константный буфер = Buffer.alloc(8) console.log(buffer); // <Буфер 00 00 00 00 00 00 00 00> // Если значению присвоено десятичное число, буфер поможет нам преобразовать его в шестнадцатеричное число, а затем записать в соответствующее место buffer[0] = 88 // В js все, что начинается с 0x, представлено как шестнадцатеричное число buffer[1] = 0x88 console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>
Операции с буфером и файлом
1. Если в текстовом файле
buffer
. , который является результатом двоичного числа в кодировке utf-8
const fs = require('fs') fs.readFile('./a.txt', (ошибка, данные) => { console.log(данные); // <Буфер e5 93 88 e5 93 88> })
const fs = require('fs') // кодировка указывает кодировку символов, используемую для декодирования, по умолчанию используется кодировка utf-8 fs.readFile('./a.txt', { кодировка: 'utf-8' }, (ошибка, данные) => { console.log(data); // Ха-ха})
const fs = require('fs') // В кодировке используется кодировка символов utf16le, а в декодировании используется формат utf-8. Должно быть, декодирование неправильное. fs.readFile('./a.txt', {coding: 'utf16le' }, (err. , данные) => { console.log(данные); // Ошибка }) // Приведенный выше код аналогичен следующему коду const msg = 'Ха-ха' const buffer = Buffer.from(msg, 'utf-8') console.log(buffer.toString('utf16le')); //
2. Файл изображения
копирует кодировку изображения для достижения цели копирования изображения.
encoding
, поскольку кодировка символов. читается только при чтении изображения. Это полезно только при получении текстовых файлов.const fs = require('fs'). fs.readFile('./logo.png', (ошибка, данные) => { console.log(data); // Выводится двоичная кодировка, соответствующая файлу изображения // Мы также можем записать кодировку изображения в другой файл, что эквивалентно копированию изображения fs.writeFile(' ./bar .png', данные, ошибка => { console.log(ошибка); }) })
sharp
const Sharp = require('sharp') // Обрезаем изображение logo.png до размера 200x300 и копируем его в файл bax.png Sharp('./logo.png') .resize(200, 300) .toFile('./bax.png', (ошибка, информация) => { console.log(ошибка); }) // Вы также можете сначала преобразовать файл изображения в буфер, а затем записать его в файл. Вы также можете скопировать изображение Sharp('./logo.png') .resize(300, 300) .toBuffer() .then(данные => { fs.writeFile('./baa.png', данные, ошибка => { console.log(ошибка); }) })
Процесс создания буфера
Buffer
, мы не будем часто обращаться к памяти из операционной системы. По умолчанию он сначала будет обращаться к памяти размером 8 * 1024
байт, то есть 8kb
,Что такое цикл событий?
Что такое цикл событий?
JS
который мы пишем, и браузером или Node
.JS
который мы пишем, и вызовами API браузера ( setTimeout
, AJAX
,监听事件
и т. д.). ) Мосты взаимодействуют через функции обратного вызова.file system
, networ
и т. д.). Мосты также взаимодействуют через функции обратного вызова.Процесс и поток
Процесс и поток — это две концепции в операционной системе:
process
): программа, которую компьютер запускаетthread
): наименьшая единица, с которой операционная система может запускать расписание вычислений, поэтому CPU
может работать напрямую. поток, которыйзвучит очень абстрактно, давайте объясним это интуитивно:
Давайте на ярком примере объясним
многопроцессная многопоточная
операционная система.Как можно выполнять несколько процессов (при прослушивании музыки, написании кода). , и проверка информации) работают одновременно?
CPU
очень высока и он может быстро переключаться между несколькими процессами.Браузеры и JavaScript
Мы часто говорим, что JavaScript
является однопоточным, но поток JS должен иметь собственный процесс-контейнер Node
Является ли браузер или браузер Node процессом?
tab
, запускается новый процесс. Это необходимо для предотвращения зависания одной страницы и зависания всех страниц. Необходимо принудительно закрытьОднако выполнение кода JavaScript выполняется в отдельном потоке.
JS
.процесса выполнения JavaScript
не будет выполнена, пока она не будет помещена в стек вызовов функций. Давайте проанализируем процесс выполнения кода
const message = 'Hello World'. console.log(сообщение); функция сумма (число1, число2) { вернуть число1 + число2 } функция Фу() { константный результат = сумма (20, 30) console.log(результат); } foo()
main
функции, как и в других языках программированияmessage
log
. Функция будет помещена. Введите стек вызовов функций. После выполнения извлеките стекfoo
. Функция foo помещается в стек вызовов функций. Однако функцию sum
необходимо вызвать во время выполнения,js
код, а основная функция выскакивает изцикла событий браузера
. Что делать, если во время выполнения JS
кода происходят асинхронные операции?
setTimeout
в серединеЗатем функция передается в функцию setTimeout
.(мы называем это функцией timer
), когда она будет выполнена?
web api
. Браузер заранее сохранит функцию обратного вызова. В подходящее время функция таймера будет добавлена в очередь событий.Почему setTimeout не блокирует выполнение кода,
Это потому, что браузер поддерживает очень, очень важную вещь —
браузер цикла событий поможет нам каким-то образом сохранить функцию обратного вызова в setTimeout. Более распространенный метод — сохранить ее в красно-черном дереве
и дождаться запланированного setTimeout. Когда наступит время таймера, наша функция обратного вызова таймера будет удалена из сохраненного места и помещена в очередь событий.
Как только цикл событий обнаружит, что в нашей очереди что-то есть, а текущий стек вызовов функции пуст, другое.
синхронизации
функции обратного вызова в нашей очереди будут исключены из очереди и помещены в стек вызовов функций для выполнения (следующая функция не будет помещена в стек до тех пор, пока не выйдет предыдущая функция в очереди).
, нет. Должно быть только одно событие. Например, во время определенного процесса пользователь нажимает кнопку в браузере. У нас может быть монитор нажатия этой кнопки, который соответствует этой функции обратного вызова. также будут добавлены в нашу очередь. Порядок выполнения зависит от порядка их расположения в очереди событий. Существует также сводка обратных вызовов, которые мы отправляем ajax
запросы в очередь событий
: На самом деле цикл событий — это очень простая вещь. Это означает, что когда определенный обратный вызов должен быть выполнен в особой ситуации, он будет сохранен. заранее помещается в очередь событий, а цикл событий извлекает его и помещает в стек вызовов функций.
Макрозадачи и микрозадачи.
Однако цикл событий не поддерживает только одну очередь. На самом деле очередей две, и выполнение задач в очереди должно ждать,
macrotask queue
ajax
setTimeout
, setInterval
, мониторинг DOM
, UI Rendering
и другиеmicrotask queue
): then
вызов Promise
, Mutation Observer API
, queueMicrotask()
и т. д.Итак, каков приоритет двух очередей в цикле событий?
main script
выполняется первым (написан код скрипта верхнего уровня)..
Тестовые точки: main stcipt
, setTimeout
, Promise
, then
, queueMicrotask
setTimeout(() => { console.log('set1');4 новое обещание (решить => { решать() }).then(решить => { новое обещание (решить => { решать() }).then(() => { console.log('then4'); }) console.log('then2'); }) }) новое обещание (решить => { console.log('pr1'); решать() }).then(() => { console.log('then1'); }) setTimeout(() => { console.log('set2'); }) консоль.журнал(2); очередьМикротаск(() => { console.log('queueMicrotask'); }) новое обещание (решить => { решать() }).then(() => { console.log('then3'); }) // пр1 // 2 //тогда1 //очередьМикрозадача //тогда3 // набор1 //тогда2 //тогда4 // set2
setTimeout
будет немедленно помещен в стек вызова функции и будет извлечен из стека сразу после выполнения. Его функция timer
будет помещена в очередь задач макроса.
Функция, переданная в класс Promise
, будет выполнена немедленно. Это не функция обратного вызова, поэтому будет напечатан pr1
, и поскольку метод resolve
будет выполнен, статус Promise немедленно изменится на fulfilled
, так что при выполнении функции then
будет выполнена соответствующая функция обратного вызова. быть помещен в очередь микрозадач
, и функция setTimeout встречается снова. При извлечении стека ее функция таймера будет помещена в очередь макрозадач
, когда она встретит оператор console.log
. После того, как функция будет помещена в стек, 2
распечатывается, а затем выскакивает.
Здесь функция привязана к queueMicrotask
, и функция будет помещена. После входа в очередь микрозадач
был обнаружен новый оператор Promise, но он сразу же изменил статус обещания на выполненное, поэтому обратный вызов соответствующая функции then также была помещена в очередь микрозадач.
Поскольку код сценария синхронизации был выполнен, теперь событие В начале цикла задачи, конкурирующие с очередью микрозадач, и макрозадача помещаются в очередь. стек вызовов функций в порядке приоритета. Примечание. Приоритет микрозадач выше, чем у макрозадач. Вы должны читать его каждый раз, прежде чем выполнять макрозадачу. Проверьте, пуста ли очередь микрозадач. он не пуст, вам нужно сначала выполнить задачу очереди микрозадач.
Первая микрозадача — напечатать then1
, вторая микрозадача — напечататьqueueMicrotask, а третья микрозадача — напечатать then3
. После этого выполнение завершается. начать выполнение макрозадачи.
Первая макрозадача более сложная. Сначала она выведет set1
, а затем выполнит new promise
, который немедленно изменит состояние. Затем обратный вызов будет помещен в очередь микрозадач. Очередь не пуста, поэтому необходимо выполнить очередь микрозадач с более высоким приоритетом, что эквивалентно немедленному выполнению обратного вызова then. Это тот же новый оператор Promise, и соответствующий ему обмен then помещается в очередь микрозадач. Обратите внимание, что после нового оператора Promise есть console
функция. Эта функция будет выполнена сразу после выполнения нового оператора Promise, то есть вывода then2
. В сопоставлении микрозадач все еще есть задача, поэтому следующим шагом будет печать. then4
. На данный момент очередь микрозадач пуста, и очередь макрозадач может продолжать выполняться
, поэтому будет напечатан следующий set2
макрозадач2. После выполнения макрозадачи
результат печати всего кода: pr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2
Вопросы для собеседования <2>
Тестовые точки: main script
, setTimeout
, Promise
, then
, queueMicrotask
, await
async
дополнение к асинхронным знаниям: async, await — это синтаксический сахар for Promise
. При возникновении проблем с циклом событий
new Promise((resolve,rejcet) => { 函数执行})
then(res => {函数执行})
в предыдущем Promiseasync function async1() { console.log('async1 start'); дождитесь асинхронного2() console.log('конец async1'); } асинхронная функция async2() { console.log('async2'); } console.log('запуск скрипта'); setTimeout(() => { console.log('setTimeout'); }, 0) асинхронный1() новое обещание (решить => { console.log('обещание1'); решать() }).then(() => { console.log('обещание2'); }) console.log('конец сценария'); // запуск скрипта // начало асинхронного1 // асинхронный2 // обещание1 // конец скрипта // конец асинхронного1 // обещание2 // setTimeout
— это определение функции в начале, и его не нужно помещать в стек вызовов функций для выполнения до тех пор, пока он не встретит первый оператор console
. После помещения стека выполните script start
печати, а затем извлеките его из стека. стек
для обнаружения первой функции setTimeout
, которая соответствует timer
будет помещен в очередь задач макроса
, и будет выполнена функция async1. Сначала будет напечатан async1 start
, а затем будет выполнена функция async2
после оператора await
. Поскольку, как упоминалось ранее, функция после ключевого слова await считается new Promise
, эта функция будет выполнена немедленно, поэтому будет распечатан async2, но код после оператора await эквивалентен помещению в then. обратный вызов, то есть console.log('async1 end')
Эта строка кода помещается в очередь микрозадач
, и код продолжает выполняться. Он встречает новый оператор Promise, поэтому функция promise1
немедленно выводится. затем обратный вызов помещается в очередь микрозадач для
выполнения последней консольной функции для script end
, код синхронизации будет выполнен. Цикл событий перейдет к очередям макро-задач и микро-задач для выполнения задач
. Очередь задач будет выполнена, что означает, что будет напечатан async1 end
, а затем напечатано promise2
. В это время очередь микрозадач пуста, и начинаются задачи в очереди макрозадач. быть выполнено.
Будет напечатано значение setTimeout, соответствующее функции таймера. В это время также выполняется макрозадача, и окончательная последовательность печати: script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout