В прошлый раз, когда все снова ели и пили PromiseA+ со мной, я думаю, все должны думать об арбузной коле...
Какая арбузная кола! Это явно Обещание!
Э-э, протрезвейте, сегодня все передвинули небольшую скамейку и послушайте, как я рассказываю о более интересных циклах событий в JS. Прежде чем мы разберемся с циклом событий, давайте сначала разберемся с несколькими основными понятиями.
Куча
Стек представляет собой набор данных в порядке поступления (LIFO). Новые добавленные или удаленные элементы хранятся в конце стека, который называется вершиной стека, а другой конец называется низом стека. В стеке новые элементы располагаются ближе к вершине стека, а старые элементы ближе к низу стека.
Я не думаю, что это легко понять. Давайте возьмем пример. Например, есть коробка для пинг-понга. Мы продолжаем класть шарики для пинг-понга в коробку. Тот, который входит, должен быть наверху, поэтому, если мы хотим чтобы вынуть эти шарики, мы должны вынимать их сверху вниз?Это модель LIFO, то есть шарики, которые входят в коробку с шариками, выходят первыми.
Концепция стека на самом деле очень важна в нашем js.Все мы знаем, что наш js — это однопоточный язык, так где же его единственный поток, только в его основном рабочем потоке, который является контекстом выполнения, о котором мы часто говорим, это выполнение Контекст — это пространство стека, давайте посмотрим на кусок кода:
console.log('1');
function a(){
console.log('2');
function b(){
console.log('3')
}
b()
}
a()
Мы знаем, что когда функция выполняется, функция будет помещена в наш контекст выполнения, а стек выполнения будет всплывать после выполнения функции.Тогда в соответствии с этим принципом мы можем знать, что запущенный процесс этого кода
- Прежде всего, когда наш код будет выполняться, будет глобальный контекст, в это время код выполняется, и глобальный контекст выполняет стек, который находится внизу стека.
- мы встретились
console.log('1')
, Эта функция попадает в стек выполнения при ее вызове.При выполнении этого предложения, то есть при достижении следующей строки, мыconsole
Эта функция выскочит из стека, в это время в стеке остается только глобальный контекст - Затем запустите код.Обратите внимание, что объявления функций, с которыми мы сталкиваемся, не войдут в стек выполнения, только когда наша функция будет вызвана и выполнена.Этот принцип точно такой же, как имя нашего стека выполнения, а затем мы встретили
a();
В это время наша функция входит в стек выполнения, а затем входит в нашa
Внутри функции наш стек выполнения функции должен быть全局上下文 —— a
- Затем я бегу
console.log('2')
, стек выполнения становится全局上下文——a——console
, то нашconsole
После запуска мы восстанавливаем стек выполнения в全局上下文 —— a
- Потом мы встретились
b()
;Такb
в наш стек выполнения,全局上下文——a——b
, - Затем войдите внутрь функции b и выполните
console.log('3')
Когда стек выполнения全局上下文——a——b——console
, после завершения выполнения ответ全局上下文——a——b
- тогда наш
b
Функция выполняется, а затем извлекается из стека выполнения, после чего стек выполнения становится全局上下文——a
- тогда наш
a
Функция выполняется, а затем извлекается из стека выполнения, после чего стек выполнения становится全局上下文
- Тогда наш глобальный контекст будет появляться при закрытии нашего браузера.
Процесс выполнения нашего контекста выполнения выглядит так, он намного понятнее~
Из приведенного выше контекста выполнения мы можем найти несколько характеристик:
- Контекст выполнения является однопоточным.
- Контекст выполнения заключается в синхронном выполнении кода.
- Когда функция вызывается, функция входит в контекст выполнения
- Когда код запускается, он генерирует глобальный контекст, который появляется из стека только при закрытии браузера.
Очередь
Очередь — это набор данных, который следует принципу «первым поступил — первым обслужен» (FIFO).Новые записи добавляются в конец очереди, а старые удаляются из начала очереди.
Здесь мы видим, что разница между очередью и стеком заключается в том, что стек — это LIFO, похожий на коробку для пинг-понга, а очередь — FIFO, что означает, что первый, кто войдет, будет первым, кто выйдет. Точно так же возьмем пример.Очередь похожа на то, когда мы стоим в очереди на проверку безопасности.Первый человек, который приходит, находится в первой очереди очереди, а последний человек находится в конце очереди.Тогда инспектор безопасности будет проводить проверку безопасности с начала очереди.После освобождения одного человека освобождается один человек.Является ли такая команда процессом «первым пришел – первым вышел»?
Очередь Здесь мы упомянем два понятия: макрозадачу и микрозадачу.
очередь задач
Выполнение события Js разделено на макрозадачи и микрозадачи
- Hongrenwu в основном состоит из
script
(глобальная задача),setTimeout
,setInterval
,setImmediate
, ввод-вывод, рендеринг пользовательского интерфейса - Микрозадачи в основном
process.nextTick
,Promise.then
,Object.observer
,MutationObserver
.
цикл событий браузера
В процессе выполнения кода js, если он встретит вышеуказанный код задачи, он сначала поместит обратный вызов этих кодов в соответствующую очередь задач, а затем продолжит выполнять код основного потока, пока не будут выполнены все функции в контексте выполнения. выполняется в очереди микрозадач, чтобы сначала выполнить связанные задачи. После того, как очередь микрозадач опустеет, она достанет задачу из очереди макрозадач и поместит ее в контекст выполнения, а затем продолжит цикл.
- Выполните код, поместите его в очередь макросов человека при встрече с благожелательностью макроса, поместите его в очередь микрозадач при встрече с микрозадачами и поместите его в контекст выполнения при выполнении других функций.
- После того, как все выполнения в контексте выполнения завершены, выполните очередь микрозадач.
- После завершения выполнения очереди микрозадач выньте первый элемент из очереди макрозадач и поместите его в контекст выполнения для выполнения.
- Затем он продолжает зацикливать шаги 1-3, что
浏览器环境
серединаjs事件环
//学了上面的事件环 我们来看一道面试题
setTimeout(function () {
console.log(1);
}, 0);
Promise.resolve(function () {
console.log(2);
})
new Promise(function (resolve) {
console.log(3);
});
console.log(4);
//上述代码的输出结果是什么???
мышление мышление мышление мышление
Правильный ответ3 4 1
, Это так же, как вы думаете? Давайте посмотрим на работающий процесс кода
// 遇到setTimeout 将setTimeout回调放入宏仁务队列中
setTimeout(function () {
console.log(1);
}, 0);
// 遇到了promise,但是并没有then方法回调 所以这句代码会在执行过程中进入我们当前的执行上下文 紧接着就出栈了
Promise.resolve(function () {
console.log(2);
})
// 遇到了一个 new Promise,不知道大家还记不记得我们上一篇文章中讲到Promise有一个原则就是在初始化Promise的时候Promise内部的构造器函数会立即执行 因此 在这里会立即输出一个3,所以这个3是第一个输入的
new Promise(function (resolve) {
console.log(3);
});
// 然后输入第二个输出4 当代码执行完毕后回去微任务队列查找有没有任务,发现微任务队列是空的,那么就去宏仁务队列中查找,发现有一个我们刚刚放进去的setTimeout回调函数,那么就取出这个任务进行执行,所以紧接着输出1
console.log(4);
Увидев приведенное выше объяснение, вы все понимаете, просто ли вызывать его напрямую ~
Затем давайте посмотрим на цикл выполнения события в среде узла.
Цикл событий NodeJs
Цикл событий браузера соответствует стандарту HTML5, а цикл событий NodeJs соответствует стандарту libuv, поэтому будут некоторые различия в выполнении событий.Все знают, что nodejs на самом деле является средой выполнения js, то есть средой выполнения, а затем В этой среде большинство API-интерфейсов nodejs выполняются через функции обратного вызова и методы публикации и подписки на события, поэтому каков порядок выполнения нашего кода в такой среде, то есть мы Как различные функции обратного вызова классифицируются и в каком порядке выполняются на самом деле определяется нашей libuv.
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
Давайте посмотрим, для чего используются эти шесть задач.
- таймеры: на этом этапе выполняются обратные вызовы, установленные setTimeout() и setInterval().
- ожидающие обратные вызовы: небольшое количество обратных вызовов ввода-вывода из предыдущего цикла будет отложено до этой стадии цикла.
- бездействие, подготовка: только для внутреннего использования.
- poll: выполнить обратный вызов ввода-вывода, который будет заблокирован на этом этапе при правильных условиях.
- check: выполнить обратный вызов, установленный setImmediate().
- обратные вызовы закрытия: выполнение обратных вызовов, таких как socket.on('close', ...).
Давайте посмотрим на схему выполнения nodejs, найденную в Интернете.Мы видим, что на диаграмме шесть шагов.При выполнении кода, если мы встречаем функцию обратного вызова в этих шести шагах, мы ставим ее в соответствующую очередь, и затем, когда код будет выполнен Когда мы синхронизируем символы, мы переключимся на следующий этап, который является этапом таймера Затем, во время выполнения этапа таймера, будут выполнены все функции обратного вызова этого этапа, а затем войдем в следующий этап.Следует отметить, что мы находимся в Каждый раз, когда этап переключается, все задачи в очереди микрозадач будут выполняться сначала, а затем переходят на следующий этап задачи, поэтому мы можем суммировать последовательность цикла событий nodejs
- Синхронизируйте выполнение кода, очистите очередь микрозадач и выполните функцию обратного вызова стадии таймера (то есть setTimeout, setInterval)
- После завершения всех выполнений очередь микрозадач очищается и выполняется функция обратного вызова стадии ожидающих обратных вызовов.
- После завершения всех выполнений очистите очередь микрозадач и выполните функции обратного вызова фаз ожидания и подготовки.
- После завершения всех исполнений очередь микрозадач очищается и выполняется callback-функция стадии опроса.
- После завершения всех исполнений очередь микрозадач очищается, и выполняется callback-функция фазы проверки (то есть setImmediate)
- После завершения всех исполнений очередь микрозадач очищается и выполняется callback-функция стадии close callbacks.
- Затем пройдите этапы 1-6.
Тогда давайте потренируем наши руки~~~
// 我们来对着我们的执行阶段看看
let fs = require('fs');
// 遇到setTimeout 放入timer回调中
setTimeout(function(){
Promise.resolve().then(()=>{
console.log('then1');
})
},0);
// 放入微任务队列中
Promise.resolve().then(()=>{
console.log('then2');
});
// i/o操作 放入pending callbacks回调中
fs.readFile('./text.md',function(){
// 放入check阶段
setImmediate(()=>{
console.log('setImmediate')
});
// 放入微任务队列中
process.nextTick(function(){
console.log('nextTick')
})
});
Прежде всего, после выполнения кода синхронизации мы сначала очищаем микрозадачу, затем выводим then2, затем переключаемся на стадию таймера, выполняем обратный вызов таймера, выводим then1, затем выполняем обратный вызов операции ввода-вывода, затем очищаем очередь микрозадачи , выведите nextTick, а затем войдите в этап проверки, очистите вывод обратного вызова этапа проверки setImmediate
Все правила кажутся расплывчатыми, но пока мы суммируем правила и понимаем механизм их действия, тогда мы и освоим эти правила.Ладно, я сегодня так многому научился, что больше не буду говорить. , поторопитесь и напишите бизнес-код......