Цель этой статьи - убедиться, что вы полностью понимаете механизм выполнения javascript.Если вы все еще не понимаете после прочтения этой статьи, вы можете побить меня.
Независимо от того, являетесь ли вы новичком в javascript или ветераном, проходите ли вы собеседование при приеме на работу или выполняете ежедневную работу по разработке, мы часто сталкиваемся с такой ситуацией: учитывая несколько строк кода, нам нужно знать содержимое и порядок вывода. Поскольку javascript является однопоточным языком, мы можем заключить, что:
- javascript выполняется в том порядке, в котором появляются операторы
Увидев это, читатель побьет людей: разве я не знаю, что js выполняется построчно? Тебе еще нужно сказать? Не волнуйтесь, потому что js выполняется построчно, поэтому мы думаем, что js выглядит так:
let a = '1';
console.log(a);
let b = '2';
console.log(b);
Однако на самом деле js выглядит так:
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
согласно сjs выполняется в том порядке, в котором появляются операторыС этой идеей я уверенно записываю вывод:
//"定时器开始啦"
//"马上执行for循环啦"
//"执行then函数啦"
//"代码执行结束"
Перейдите в хром, чтобы проверить, результат совершенно неправильный, и я мгновенно ошеломлен. А как насчет выполнения построчно?
Нам действительно нужно досконально понять механизм выполнения javascript.
1. О JavaScript
джаваскрипт этоодин потокЯзык, Web-Worker был предложен в последней версии HTML5, но ядро однопоточного javascript не изменилось. Поэтому все версии javascript «многопоточности» моделируются с помощью одного потока, а вся многопоточность javascript — это бумажный тигр!
2. Цикл событий javascript
Поскольку js является однопоточным, это похоже на банк с одним окном. Клиенты должны стоять в очереди, чтобы обрабатывать дела один за другим. Точно так же задачи js также должны выполняться одна за другой. Если одна задача выполняется слишком долго, вторая задача также должна ждать. Итак, вопрос в том, если мы хотим просматривать новости, но сверхчеткие изображения, содержащиеся в новостях, загружаются очень медленно, должна ли наша веб-страница зависать до полного отображения изображений? Итак, умные программисты делят задачи на две категории:
- Синхронизировать задачу
- асинхронная задача
Когда мы открываем веб-сайт, процесс рендеринга веб-страницы представляет собой множество синхронных задач, таких как рендеринг скелета страницы и элементов страницы. Задачи, которые занимают много ресурсов и занимают много времени, например загрузка изображений и музыки, являются асинхронными задачами. Для этой части существуют строгие текстовые определения, но цель этой статьи — досконально понять механизм выполнения с минимальными затратами на обучение, поэтому мы используем карту для иллюстрации:
Если содержание, которое должно быть выражено картой, выражено словами:
- Синхронные и асинхронные задачи попадают в разные «места» выполнения, синхронно входят в основной поток, асинхронно входят в Таблицу событий и регистрируют функции.
- Когда указанное действие будет выполнено, таблица событий переместит эту функцию в очередь событий.
- После того, как задача в основном потоке опустеет, она отправится в Очередь событий, чтобы прочитать соответствующую функцию и войти в основной поток для выполнения.
- Вышеупомянутый процесс будет продолжать повторяться, что часто называют циклом событий.
Мы не можем не спросить, а как узнать, что стек выполнения основного потока пуст? В движке js есть процесс мониторинга, который будет постоянно проверять, пуст ли стек выполнения основного потока, а когда он пуст, он переходит в очередь событий, чтобы проверить, есть ли функции, ожидающие вызова.
Сказав так много текста, проще направить кусок кода:
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('发送成功!');
}
})
console.log('代码执行结束');
Вышеупомянутое простоajax
Код заявки:
- ajax входит в таблицу событий и регистрирует функцию обратного вызова
success
. - воплощать в жизнь
console.log('代码执行结束')
. - Событие ajax завершено, функция обратного вызова
success
Войдите в очередь событий. - Основной поток считывает функцию обратного вызова из очереди событий.
success
и выполнить.
Я полагаю, что благодаря приведенному выше тексту и коду у вас есть предварительное представление о порядке выполнения js. Далее давайте изучим более сложную тему: setTimeout.
3. Любовь-ненависть setTimeout
известныйsetTimeout
Излишне говорить, что первое впечатление о нем у всех такое, что асинхронное выполнение может быть отложено.Мы часто реализуем отложенное выполнение на 3 секунды так:
setTimeout(() => {
console.log('延时3秒');
},3000)
ПостепенноsetTimeout
Много где он используется, и тоже возникает проблема.Иногда пишут, что задержка 3 секунды, а реальная функция выполняется за 5 или 6 секунд.Что происходит?
Давайте посмотрим на пример:
setTimeout(() => {
task();
},3000)
console.log('执行console');
Согласно нашим предыдущим выводам,setTimeout
является асинхронным и должен выполняться первымconsole.log
Это синхронная задача, поэтому наш вывод:
//执行console
//task()
Проверьте, результат правильный! Затем мы модифицируем предыдущий код:
setTimeout(() => {
task()
},3000)
sleep(10000000)
На первый взгляд почти то же самое, но когда мы выполняем этот код в хроме, мы обнаруживаем, что консоль выполняетtask()
Требуемое время намного больше, чем 3 секунды.Обещанная задержка 3 секунды.Почему сейчас так долго?
В это время нам нужно переосмыслитьsetTimeout
Определение. Давайте сначала поговорим о том, как выполняется приведенный выше код:
-
task()
Войдите в таблицу событий и зарегистрируйтесь, время начинается. - воплощать в жизнь
sleep
функция, очень медленно, очень медленно, время продолжается. - 3 секунды истекли, событие по времени
timeout
Заканчивать,task()
в очередь событий, ноsleep
Это слишком медленно, это еще не закончено, так что я должен ждать. -
sleep
наконец закончено,task()
Наконец-то вошел в выполнение основного потока из очереди событий.
После завершения вышеуказанного процесса мы знаем, чтоsetTimeout
Эта функция предназначена для выполнения задачи, которая должна быть выполнена через указанное время (в этом примереtask()
) добавляется в очередь событий, и поскольку это однопоточная задача, которую необходимо выполнять одну за другой, если предыдущая задача занимает слишком много времени, вы можете только ждать, в результате чего реальное время задержки намного превышает 3 секунды. .
Мы также часто сталкиваемсяsetTimeout(fn,0)
Что значит выполнить такой код через 0 секунд? Можно ли его выполнить немедленно?
Ответ - нет,setTimeout(fn,0)
Смысл в том, чтобы указать, что задача выполняется в самое раннее доступное время простоя основного потока, а это означает, что нет необходимости ждать, сколько секунд, пока задачи синхронизации в стеке выполнения основного потока находятся все выполнено, стек будет выполнен немедленно. Например:
//代码1
console.log('先执行这里');
setTimeout(() => {
console.log('执行啦')
},0);
//代码2
console.log('先执行这里');
setTimeout(() => {
console.log('执行啦')
},3000);
Вывод кода 1:
//先执行这里
//执行啦
Вывод кода 2:
//先执行这里
// ... 3s later
// 执行啦
оsetTimeout
Следует добавить, что даже если основной поток пуст, 0 миллисекунд на самом деле недостижим. Согласно стандартам HTML, минимум составляет 4 миллисекунды. Заинтересованные студенты могут узнать об этом самостоятельно.
4. Ненавижу и люблю setInterval
вышеизложенное закончилосьsetTimeout
, конечно, чтобы не упустить своего брата-близнецаsetInterval
. Они похожи, за исключением того, что последний является выполнением цикла. Для исполнительного листа,setInterval
Зарегистрированная функция будет помещаться в Очередь событий каждое указанное время.Если предыдущая задача занимает слишком много времени, ее также нужно подождать.
Единственное, что следует отметить, это то, что дляsetInterval(fn,ms)
Например, мы уже знаем, что не каждыйms
будет выполняться один раз в секундыfn
, но каждый разms
секунды, будетfn
Войдите в очередь событий. однаждыsetInterval
функция обратного вызоваfn
Время выполнения превысило время задержкиms
, то разрыва во времени нет вообще.. Читателей просят внимательно смаковать это предложение.
5.Promise и process.nextTick(обратный вызов)
Мы уже изучили традиционный таймер, а затем изучимPromise
а такжеprocess.nextTick(callback)
Представление.
Promise
Определение и функцииPromise. а такжеprocess.nextTick(callback)
Подобно версии setTimeout для node.js, функция обратного вызова вызывается на следующей итерации цикла обработки событий.
Переходим к сути, помимо обобщенных синхронных задач и асинхронных задач у нас есть более уточненное определение задач:
- макрозадача (макрозадача): включая общий код скрипта, setTimeout, setInterval
- микрозадача: Promise, process.nextTick
Различные типы задач будут поступать в соответствующую очередь событий, напримерsetTimeout
а такжеsetInterval
попадет в ту же очередь событий.
Порядок цикла событий определяет порядок выполнения кода js. После ввода общего кода (макрозадачи) запустите первый цикл. Затем выполните все микрозадачи. Затем снова начните с макрозадачи, обнаружите, что одна из очередей задач была выполнена, а затем выполните все микрозадачи. Звучит немного запутанно, давайте проиллюстрируем фрагмент кода в начале статьи:
setTimeout(function() {
console.log('setTimeout');
})
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');
- Этот код входит в основной поток как задача макроса.
- встретимся первым
setTimeout
, затем зарегистрируйте его функцию обратного вызова и распределите ее по задаче макроса Event Queue. (Процесс регистрации такой же, как описано выше, и не будет описан ниже) - встретились дальше
Promise
,new Promise
выполнить немедленно,then
Функция отправляется в очередь событий микрозадачи. - встретить
console.log()
, выполнить немедленно. - Итак, в качестве первой макрозадачи выполняется весь скрипт кода, посмотрим, какие там микрозадачи? мы нашли
then
В очереди событий микрозадачи выполните. - ок, первый виток цикла событий закончен, начинаем второй виток цикла, естественно, начиная с макрозадачи Event Queue. Мы нашли макро-задачу Event Queue в
setTimeout
Соответствующая функция обратного вызова выполняется немедленно. - конец.
Связь между циклом событий, макрозадачей и микрозадачей показана на рисунке:
Давайте проанализируем более сложный фрагмент кода, чтобы увидеть, действительно ли вы владеете механизмом выполнения js:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
Первый раунд анализа процесса цикла событий выглядит следующим образом:
- Общий сценарий входит в основной поток в качестве первой задачи макроса и встречает
console.log
, выход 1. - встретить
setTimeout
, а его функция обратного вызова отправляется задаче макроса Event Queue. Мы временно записываем какsetTimeout1
. - встретить
process.nextTick()
, а его функция обратного вызова отправляется в очередь событий микрозадачи. мы записываем какprocess1
. - встретить
Promise
,new Promise
Выполнить напрямую, вывод 7.then
Распространяется на очередь событий микрозадачи. мы записываем какthen1
. - встретились снова
setTimeout
, его callback-функция отправляется в макрозадачу Event Queue, мы записываем ее какsetTimeout2
.
Задача макроса Очередь событий | Очередь событий микрозадач |
---|---|
setTimeout1 | process1 |
setTimeout2 | then1 |
-
В приведенной выше таблице показано состояние каждой очереди событий в конце первого раунда макрозадач цикла событий, и в это время были выведены 1 и 7.
-
мы нашли
process1
а такжеthen1
Две микрозадачи. -
воплощать в жизнь
process1
, вывод 6. -
воплощать в жизнь
then1
, выход 8.
Что ж, первый раунд цикла событий официально завершен, результатом этого раунда является вывод 1, 7, 6 и 8. Затем начинается второй раунд временной петли.setTimeout1
Запускается задача макроса:
- Первый выход 2. встретились дальше
process.nextTick()
, а также распределять его в Очередь событий микрозадачи, обозначаемую какprocess2
.new Promise
Немедленно выполнить выход 4,then
Также распространяется на очередь событий микрозадач, записанная какthen2
.
Задача макроса Очередь событий | Очередь событий микрозадач |
---|---|
setTimeout2 | process2 |
then2 |
- В конце второго раунда макрозадач цикла событий мы обнаружили, что есть
process2
а такжеthen2
Можно выполнить две микрозадачи. - вывод 3.
- вывод 5.
- Второй раунд цикла событий заканчивается, и второй раунд выводит 2, 4, 3, 5.
- Начинается третий раунд цикла событий, и в это время остается только setTimeout2, и он выполняется.
- Выведите 9 напрямую.
- Буду
process.nextTick()
Распространяется на очередь событий микрозадачи. отмечен какprocess3
. - прямое исполнение
new Promise
, выход 11. - Буду
then
Распространяется на очередь событий микрозадач, записывается какthen3
.
Задача макроса Очередь событий | Очередь событий микрозадач |
---|---|
process3 | |
then3 |
- Третий раунд выполнения макрозадач цикла событий заканчивается, и выполняются две микрозадачи.
process3
а такжеthen3
. - вывод 10.
- вывод 12.
- Третий раунд цикла событий заканчивается, и третий раунд выводит 9, 11, 10 и 12.
Весь код имеет в общей сложности три цикла событий, а полный вывод равен 1, 7, 6, 8, 2, 4, 3, 5, 9, 11, 10, 12. (Обратите внимание, что мониторинг событий в среде узла зависит от libuv, а среда внешнего интерфейса не совсем такая же, и в порядке вывода могут быть ошибки)
6. Пишите в конце
(1) Асинхронный js
Мы с самого начала говорили, что javascript — это однопоточный язык, какой бы ни была так называемая асинхронность, реализованная новым фреймворком и новым синтаксическим сахаром, на самом деле он моделируется синхронным методом, очень важно твердо ухватиться за одну нить.
(2) Цикл событий Цикл событий
Цикл событий — это способ для js реализовать асинхронность, а также механизм выполнения js.
(3) Выполнение и работа javascript
Между выполнением и запуском есть большая разница.JavaScript выполняется по-разному в разных средах, таких как node, браузер, Ringo и т. д. Операция в основном относится к унифицированному механизму синтаксического анализа javascript.
(4)setImmediate
Существует много типов микрозадач и макрозадач, таких какsetImmediate
Подождите, в реализации есть что-то общее, и заинтересованные студенты могут разобраться в этом сами.
(5) Последний из последних
- javascript - это однопоточный язык
- Цикл событий — это механизм выполнения javascript.
Твердо усвойте два основных момента, серьезно сосредоточьтесь на изучении javascript и осуществите великую мечту стать мастером интерфейса как можно скорее!
- Почта для связи:ssssyoki@foxmail.com
- Свяжитесь с WeChat: the-UK