Эта статья представляет собой личное понимание nodejs в реальной разработке и обучении. Она составлена для дальнейшего использования. Я был бы рад, если бы она вдохновила вас.
Ввод-вывод : ввод-вывод, ввод и вывод системы.
Систему можно понимать как личность, например, человека. Когда вы говорите, это результат, а когда вы слушаете, это вход.
Разница между блокирующим вводом-выводом и неблокирующим вводом-выводом заключается в том , может ли система получать другие входные данные в течение периода от входа до выхода .
Вот два примера, иллюстрирующие, что такое блокирующий ввод-вывод и неблокирующий ввод-вывод:
1. Приготовление пищи.
Прежде всего, нам нужно определить область действия системы. В этом примере тетушка из столовой и официант в ресторане рассматриваются как система. Входные данные — это заказ, а выходные — подача блюд .
Затем, можете ли вы принимать заказы других людей между заказом и подачей еды, вы можете определить, является ли это блокирующим или неблокирующим вводом-выводом.
Что касается тёти из столовой, она не может сделать заказ для других учеников при заказе. Только после того, как ученик закончил делать заказ и подал блюда, она может принять заказ следующего ученика, поэтому тётя из столовой блокирует ввод-вывод.
Официант ресторана может обслужить следующего гостя после заказа и до того, как гость подаст блюдо, поэтому у официанта есть неблокирующий ввод-вывод.
2. Делайте работу по дому
При стирке белья не нужно ждать стиральную машину. В это время можно подмести пол и привести в порядок стол. После того, как наведете порядок на столе, одежду можно постирать и ее можно повесить сушиться. Всего 25 минут.
Стирка на самом деле представляет собой неблокирующий ввод-вывод. Между загрузкой одежды в стиральную машину и завершением стирки вы можете делать и другие вещи.
Причина, по которой неблокирующий ввод-вывод может повысить производительность, заключается в том, что он позволяет избежать ненужного ожидания.
Ключом к пониманию неблокирующего ввода-вывода является
Как отражается неблокирующий ввод-вывод nodejs? Как упоминалось ранее, важным моментом в понимании неблокирующего ввода-вывода является определение границы системы. Границей системы узла является основной поток .
Если диаграмма архитектуры ниже разделена в соответствии с обслуживанием потоков, пунктирная линия слева — это поток nodejs, а пунктирная линия справа — поток C++.
Теперь поток nodejs должен запросить базу данных. Это типичная операция ввода-вывода. Он не будет ждать результатов ввода-вывода и продолжит обрабатывать другие операции. Он будет распределять большой объем вычислительной мощности между другими C++. нитки для расчетов.
Подождите, пока не появится результат, и верните его в поток nodejs. Прежде чем получить результат, поток nodejs также может выполнять другие операции ввода-вывода, поэтому он не блокируется.
Поток nodejs эквивалентен тому, что левая часть — это официант, а поток C++ — повар.
Таким образом, неблокирующий ввод-вывод узла завершается вызовом рабочих потоков C++.
Итак, как уведомить поток nodejs, когда поток C++ получит результат? Ответ зависит от событий .
Блокировка: процесс находится в режиме ожидания во время ввода-вывода и ожидает завершения ввода-вывода, прежде чем перейти к следующему шагу:
неблокируемый : функция возвращается немедленно во время ввода-вывода, и процесс не ждет ввода-вывода; О, чтобы завершить.
Итак, чтобы узнать возвращаемый результат, вам нужно использовать драйвер событий .
Так называемое событие, управляемое событиями, можно понимать так же, как событие щелчка во внешнем интерфейсе. Я сначала пишу событие щелчка, но не знаю, когда оно будет запущено. Только когда оно сработает, будет запущен основной поток. выполнить функцию, управляемую событиями.
Этот режим также является режимом наблюдателя, то есть я сначала слушаю событие, а затем выполняю его при его срабатывании.
Итак, как реализовать событийный драйв? Ответ — асинхронное программирование .
Как упоминалось выше, nodejs имеет большое количество неблокирующих операций ввода-вывода, поэтому результаты неблокирующего ввода-вывода необходимо получать с помощью функций обратного вызова. Этот метод использования функций обратного вызова является асинхронным программированием . Например, следующий код получает результат через функцию обратного вызова:
glob(__dirname+'/**/*', (err, res) => { результат = разрешение console.log('получить результат') })
. Первый параметр функции обратного вызова nodejs — это ошибка, а последующие параметры — результат . Зачем это делать?
пытаться { интервью(функция () { console.log('улыбка') }) } поймать (ошибиться) { console.log('cry', ошибка) } функциональное интервью (обратный вызов) { setTimeout(() => { если(Math.random() <0,1) { обратный вызов («успех») } еще { выдать новую ошибку («не удалось») } }, 500) }
После выполнения она не была перехвачена, и ошибка возникла глобально, что привело к сбою всей программы nodejs.
Он не перехватывается функцией try catch, поскольку setTimeout повторно открывает цикл событий. При каждом открытии цикла событий заново создается контекст стека вызовов. Try catch принадлежит стеку вызовов предыдущего цикла событий. При выполнении функции обратного вызова setTimeout. контекст стека вызовов. Все по-другому. В этом новом стеке вызовов нет try catch, поэтому ошибка выдается глобально и не может быть обнаружена. Подробную информацию см. в этой статье. Проблемы при выполнении try catch в асинхронной очереди.
Так что же делать? Передайте ошибку в качестве параметра:
functioninterview(callback) { setTimeout(() => { если(Math.random() <0,5) { обратный вызов («успех») } еще { обратный вызов (новая ошибка («сбой»)) } }, 500) } интервью (функция (рез) { если (рез. Ошибка экземпляра) { console.log('плачу') возвращаться } console.log('улыбка') })
Но это более хлопотно, и вам придется принять решение в обратном вызове, поэтому есть устоявшееся правило. Первый параметр — err. Если он не существует, это означает, что выполнение выполнено успешно.
функциональное интервью (обратный вызов) { setTimeout(() => { если(Math.random() <0,5) { обратный вызов (ноль, 'успех') } еще { обратный вызов (новая ошибка («сбой»)) } }, 500) } интервью (функция (рез) { если (рез) { возвращаться } console.log('улыбка') })Метод записи обратного вызова для
nodejs не только приведет к возникновению области обратного вызова, но и приведет к проблеме управления асинхронным процессом .
Асинхронное управление процессом в основном относится к тому, как обрабатывать логику параллелизма при его возникновении. Продолжая использовать приведенный выше пример, если ваш коллега проводит собеседование в двух компаниях, третья компания не даст ему собеседование, пока он не проведет успешное собеседование в двух компаниях. Итак, как написать эту логику? Вам нужно добавить счетчик переменных глобально:
var count = 0 интервью((ошибка) => { если (ошибка) { возвращаться } считать++ если (счет >= 2) { // Логика обработки} }) интервью((ошибка) => { если (ошибка) { возвращаться } считать++ если (счет >= 2) { // Логика обработки} })
Писать так, как описано выше, очень хлопотно и некрасиво. Поэтому методы написания Promise и async/await появились позже.
том, что текущий цикл событий не может получить результат, но будущий цикл событий даст вам результат. Это очень похоже на то, что сказал бы подонок.
Обещание — это не только отморозок, но и конечный автомат:
const pro = new Promise((resolve, ignore) => { setTimeout(() => { решить('2') }, 200) }) console.log(pro) // Печать: Promise { <pending> }
Выполнение then или catch вернет новое обещание . Окончательное состояние обещания определяется результатами выполнения функций обратного вызова then и catch:
функция интервью() { вернуть новое обещание((разрешить, отклонить) => { setTimeout(() => { если (Math.random() > 0,5) { решить («успех») } еще { отклонить (новая ошибка («провал»)) } }) }) } вар обещание = интервью() вар обещание1 = обещание.тогда(() => { вернуть новое обещание((разрешить, отклонить) => { setTimeout(() => { разрешить («принять») }, 400) }) })
Состояние обещания1 определяется состоянием обещания в ответ, то есть состоянием обещания1 после выполнения обещания в ответ. Каковы преимущества этого? Это решает проблему ада обратного вызова .
вар обещание = интервью() .then(() => { ответное интервью() }) .then(() => { ответное интервью() }) .then(() => { ответное интервью() }) .catch(е => { консоль.log(е) })
Тогда если статус возвращенного промиса отклонен, то будет вызван первый catch, а последующие потом вызываться не будут. Помните: отклоненные вызовы являются первыми, а разрешенные вызовы — первыми.
Если обещание предназначено только для решения адских обратных вызовов, оно слишком мало, чтобы недооценивать обещание. Основная функция обещания — решение проблемы управления асинхронным процессом. Если вы хотите провести собеседование в двух компаниях одновременно:
functioninterview() { вернуть новое обещание((разрешить, отклонить) => { setTimeout(() => { если (Math.random() > 0,5) { решить («успех») } еще { отклонить (новая ошибка («провал»)) } }) }) } обещать .all([интервью(), интервью()]) .then(() => { console.log('улыбка') }) // Если компания отказала, поймайте ее .catch(() => { console.log('плачу') })
Что такое синхронизация/ожидание:
console.log(async function() { вернуть 4 }) console.log(function() { вернуть новое обещание((разрешить, отклонить) => { решить(4) }) })
Результат печати тот же, то есть async/await — это просто синтаксический сахар для обещания.
Мы знаем, что try catch фиксирует ошибки на основе стека вызовов и может фиксировать только ошибки выше стека вызовов. Но если вы используете await, вы можете отловить ошибки во всех функциях в стеке вызовов. Даже если ошибка возникает в стеке вызовов другого цикла событий, например setTimeout.
После преобразования кода интервью вы можете увидеть, что код был значительно упрощен.
пытаться { жду интервью(1) жду интервью(2) жду интервью(2) } catch(e => { консоль.log(е) })
А если это параллельная задача?
await Promise.all([interview(1),interview(2)])
Из-за неблокирующего ввода-вывода nodejs для получения результатов ввода-вывода необходимо использовать методы, управляемые событиями. -управляемые методы для получения результатов необходимо использовать асинхронное программирование, например функции обратного вызова. Итак, как выполнить эти функции обратного вызова, чтобы получить результаты? Затем вам нужно использовать цикл событий.
Цикл событий — это ключевая основа для реализации функции неблокирующего ввода-вывода nodejs. Неблокирующий ввод-вывод и цикл событий — это возможности, предоставляемые библиотекой C++ libuv
.
Демонстрация кода:
const eventloop = { очередь: [], петля() { while(this.queue.length) { константный обратный вызов = this.queue.shift() перезвонить() } setTimeout(this.loop.bind(this), 50) }, добавить (обратный вызов) { this.queue.push(обратный вызов) } } eventloop.loop() setTimeout(() => { eventloop.add(() => { консоль.log('1') }) }, 500) setTimeout(() => { eventloop.add(() => { консоль.log('2') }) }, 800)
setTimeout(this.loop.bind(this), 50)
гарантирует, что через 50 мс он проверит, есть ли обратный вызов в очереди, и если да, выполнит его. Это формирует цикл событий.
Конечно, реальные события намного сложнее, и существует несколько очередей. Например, есть очередь операций с файлами и очередь времени.
константный цикл событий = { очередь: [], фсКеуе: [], таймерОчередь: [], петля() { while(this.queue.length) { константный обратный вызов = this.queue.shift() перезвонить() } this.fsQueue.forEach(обратный вызов => { если (сделано) { перезвонить() } }) setTimeout(this.loop.bind(this), 50) }, добавить (обратный вызов) { this.queue.push(обратный вызов) } }
Прежде всего, мы понимаем, что такое неблокирующий ввод-вывод, то есть сразу пропускать выполнение последующих задач при обнаружении ввода-вывода и не ждать результата ввода-вывода. При обработке ввода-вывода будет вызываться зарегистрированная нами функция обработки событий, которая называется управляемой событиями. Асинхронное программирование необходимо для реализации управления событиями. Асинхронное программирование — это самое важное звено в nodejs. Оно идет от функции обратного вызова к обещанию и, наконец, к асинхронному/ожидающему (использованию синхронного метода для написания асинхронной логики).