предисловие
Потому что у Наггетс есть определенный лимит на количество слов после доработки (я тестировал примерно на 12 500 слов, так что видеть название заголовка с десятками тысяч слов, должно быть, блефует 😂) После того, как статья украшает макет, количество слов превышает Поэтому следующие части я планирую писать отдельно, чтобы лучше написать относительно углубленное содержание.【前端体系】
Содержание таких статей должно включать, но не ограничиваться заголовком, я постараюсь расширить и углубить, чтобы писать качественные статьи.
❝Онлайн скромный, если вы считаете, что эта статья полезна для вас, пожалуйста, поставьте лайк 👻
❞
От вопроса наталкивает на размышления о Event Loop
В Event Loop (опросе событий) задействовано слишком много концепций знаний. Если вы придумаете много концептуальных вещей, это будет слишком скучно и будет следовать моим идеям с самого начала, поэтому я планирую изменить его. В этой статье вы сначала решаете эту проблему в соответствии с вашим предыдущим пониманием Event Loop (опрос событий), позже я напишу, как я думаю об этой проблеме, исходя из моего понимания Event Loop. Два разных понимания, идеи сталкиваются друг с другом, у меня может быть что-то не так, а у вас тоже могут быть какие-то моменты знаний, которые раньше игнорировались.
Извините за неуместность 😅, это фото
тема
console.log('script start');
setTimeout(() => {
console.log('北歌');
}, 1 * 2000);
Promise.resolve()
.then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
async function foo() {
await bar()
console.log('async1 end')
}
foo()
async function errorFunc () {
try {
await Promise.reject('error!!!')
} catch(e) {
console.log(e)
}
console.log('async1');
return Promise.resolve('async1 success')
}
errorFunc().then(res => console.log(res))
function bar() {
console.log('async2 end')
}
console.log('script end');
Что ж, вы можете пока перестать прокручивать вниз и сначала решить эту проблему по своему разумению.
-------------------------------------------------- ------- Я разделил строку------------------------------------------------------ -----
Я считаю, что этот вопрос определенно не является сложным для всех, но как вы решаете эту проблему? На самом деле, этот вопрос проверяет много ваших знаний.Теперь я использую свое понимание, чтобы рассказать о размышлениях по этому вопросу. Уровень ограничен, любые вопросы можно задавать в комментариях.
JS-операционный механизм
Давайте сначала объясним значение слов, которые появляются на картинке выше.
Heap (куча), Stack (стек), Queue (очередь), Event Loop (опрос событий)
очередь стека в программе
Куча (куча)
Куча — это динамическая структура хранения, набор данных, поддерживаемый полным двоичным деревом.Существует два типа кучи, одна длямаксимальная куча, а длямин куча, самая большая куча корневого узла называетсямаксимальная кучаилибольшая куча корней, наименьшая куча в корневом узле называетсямин кучаилинебольшая корневая куча. кучаЛинейная структура данных, эквивалентноодномерный массив, только с одним преемником.
максимальная куча
Куча
Настройка стека в программе представляет собой линейный список, который ограничивает операции вставки или удаления только в конце списка. Стек — это структура данных, которая следуетпоследний пришел первый вышел(LIFO: last-in-first-out
) для хранения данных, данные, введенные первыми, помещаются в нижнюю часть стека, последние данные находятся в верхней части стека, а данные извлекаются из верхней части стека, когда данные необходимо прочитать.
Стек — это специальный линейный список, который можно вставлять и удалять только с одного конца.
Очередь)
Очереди уникальны тем, что они разрешены только перед столом (front
) для операций удаления, а в бэкенде таблицы (rear
) для операций вставки, и, как и стек, очередь представляет собой линейный список с ограниченными операциями.
Конец, выполняющий операцию вставки, называется хвостом очереди, а конец, выполняющий операцию удаления, называется началом очереди. Когда в очереди нет элементов, она вызываетсяпустая очередь.
Элементы данных очереди также называются элементами очереди. Вставка элемента очереди в очередь называется постановкой в очередь, а удаление элемента очереди из очереди называется удалением из очереди. Поскольку очередь может быть вставлена только на одном конце и удалена на другом конце, только те элементы, которые входят в очередь первыми, могут быть удалены из очереди первыми, поэтому очередь также называетсяПервый первый вышел(FIFO: first-in-first-out
)
очередь стека в js
я объясню нижеязык JavaScriptКуча, стек, очередь в .
куча
Куча, динамически выделяемая память, размер не фиксирован и не будет автоматически освобождаться, хранитьсятип ссылки, относится к тем объектам, которые могут состоять из нескольких значений, которые хранятся в куче памяти и содержат переменные ссылочного типа, которые фактически хранят не саму переменную, а указатель на объект. Это можно просто понять как хранение блоков кода.
Роль кучи: хранить данные значений ссылочного типа
let obj = {
name: '北歌',
puslic: '前端自学驿站'
}
let func = () => {
console.log('hello world')
}
куча
Стек в js следует называть стеком вызовов (EC Stack), который будет автоматически выделять место в памяти, автоматически освобождать его и сохранять.базовый тип, простой сегмент данных, занимающий пространство фиксированного размера.
Роль стека: для хранения значений базового типа тоже очень важная роль.Предоставляет среду для выполнения кода
очередь
Очередь в js можно назвать какочередь задачилиАсинхронная очередь, обратные вызовы, зарегистрированные различными асинхронными операциями, хранятся в очереди задач, которая делится на два типа задач, макро-задачи (macroTask
) и микрозадачи (microTask
).
Хорошо, давайте вернемся к делу.
Почему появляется цикл событий
Хорошо известно, что JS — это однопоточный неблокирующий язык сценариев, а Event Loop — это решение для асинхронного программирования JS.
Почему JS является однопоточным языком и как он реализует операции асинхронного программирования (неблокирующие)
Первый вопрос: JavaScript был создан для обработки взаимодействия с веб-страницами браузера (обработка операций DOM, анимация пользовательского интерфейса и т. д.), и причина его разработки в виде одного потока состоит в том, чтобы не делать браузер слишком сложным, потому что несколько потоков необходимо совместно использовать ресурсы, и можно изменить результаты работы друг друга (два потока, изменяющие один и тот же узел DOM, вызовут ненужные проблемы), что слишком сложно для языка веб-скриптов.
Второй вопрос: JavaScript является однопоточным, но среда хоста, в которой он работает, — браузер многопоточный, и браузер предоставляет различные потоки для планирования цикла событий, чтобы координировать однопоточный запуск JS без блокировки.
резюме
Сначала резюмируем волну личного понимания механизма работы JS:
Выполнение кода открывает глобальный стек вызовов (основной стек), чтобы обеспечить среду для выполнения кода. В процессе выполнения код синхронной задачи выполняется немедленно. При обнаружении асинхронной задачи асинхронный обратный вызов регистрируется в очереди задач. , и выполняется асинхронный код, чтобы проверить, является ли он асинхронным или нет.Завершено, если завершено, обратный вызов текущей асинхронной задачи будет взят в основной стек для выполнения
процессы и потоки
Процесс: процесс — это наименьшая единица распределения ресурсов ЦП (наименьшая единица, которая может владеть ресурсами и работать независимо).
Поток: поток — это наименьшая единица планирования ЦП (поток — это единица выполнения программы, основанная на процессе).
Точного и единого описания процессов и потоков нет, но его можно легко понять:
Например, приложение: такое как QQ, процесс будет открыт при запуске браузера, и процесс может иметь несколько потоков для планирования и распределения ресурсов, чтобы выполнить функцию запуска программы.
В более общем плане: откройте приложение QQ, чтобы открыть процесс для запуска программы (QQ), существует несколько потоков для планирования и распределения ресурсов (несколько потоков для выделения памяти, занимаемой открытием QQ), для достижения работающей программы (QQ) роль.
На примере операционной системы:
Потоки зависят от процессов.У процесса может быть один или несколько потоков, но поток может принадлежать только одному процессу.
JS один поток
Единственный поток js означает, что движок javaScript имеет только один поток.
Однопоточный означает, что все задачи должны быть поставлены в очередь, а следующая задача будет выполняться после завершения предыдущей задачи. Если первая задача занимает много времени, вторая задача должна ждать вечно. Движок js выполняет асинхронный код без ожидания, потому что есть очередь задач и опрос событий.
- Очередь задач. Очередь задач — это очередь в порядке очереди, в которой хранятся различные обратные вызовы задач.
- Опрос событий: Опрос событий относится к процессу, в котором основной поток неоднократно берет задачи из очереди задач и выполняет задачи.
Многопоточность браузера
- Поток рендеринга графического интерфейса
- Отрисовка страниц, парсинг HTML, CSS, построение деревьев DOM, компоновка и рендеринг и т. д.
- Перерисовка страницы и перекомпоновка
- Взаимоисключающий с потоком движка JS, который является так называемым обновлением страницы блокировки выполнения JS.
- Поток движка JS
- Отвечает за выполнение кода JS-скрипта
- Отвечает за события квазиисполнения и готовности к выполнению, то есть когда таймер считает вверх или когда асинхронный запрос завершается успешно и возвращается правильно.
- Взаимоисключающий с потоком рендеринга GUI, слишком длительное время выполнения заблокирует рендеринг страницы.
- поток триггера события
- Отвечает за передачу подготовленных событий в поток JS-движка для выполнения.
- Когда в очередь задач добавляется несколько событий, они должны ждать в очереди (один поток JS).
- поток триггера таймера
- Отвечает за выполнение событий асинхронного таймера, таких как setTimeout, setInterval
- По истечении таймера добавьте зарегистрированный обратный вызов в конец очереди задач.
- поток HTTP-запросов
- Отвечает за выполнение асинхронных запросов.
- Когда код выполнения основного потока встречает асинхронный запрос, он передает функцию в поток для обработки.При прослушивании события изменения состояния, если есть функция обратного вызова, поток добавит функцию обратного вызова в конец очереди задач и дождитесь выполнения.
Event Loop
Ух, наконец-то к теме!
Опрос событий был очень ясно объяснен выше:
Опрос событий предназначен для устранения некоторых недостатков однопоточного javaScript для асинхронных операций, так что javaScript может быть одновременноодин поток, и абсолютноне блокируетОсновной механизм — это механизм, используемый для координации различных событий, взаимодействия с пользователем, выполнения сценариев, рендеринга пользовательского интерфейса, сетевых запросов и т. д.
Порядок выполнения Event Loop в браузере
Processing modelСпецификация определяетEveent Loop
Процесс цикла:
Пока существует четный цикл, он будет продолжать выполнять следующие шаги:
- 1. Выберите самую старую задачу в очереди задач. Пользовательский агент может выбрать любую очередь задач. Если нет необязательных задач, перейдите к шагу микрозадач ниже.
- 2. Установите для выбранной выше задачи значениеработающая задача.
- 3.Выполнить: запустить выбранную задачу.
- 4. Ставим четную петлюcurrently running taskстановится нулевым.
- 5. Удалите ранее запущенную задачу из очереди задач.
- 6. Микрозадачи: выполнениеконтрольная точка задач микрозадач. (то есть выполнение задач в очереди микрозадач)
- 7. Обновите рендеринг: его можно просто понимать как рендеринг браузера...
- 8. Если это рабочий цикл событий, но в очереди задач нет задач, иWorkerGlobalScopeЕсли флаг закрытия объекта равен true, четный цикл уничтожается, шаги прерываются, а затем определяются вWeb workersглавыrun a worker.
- 9. Вернитесь к первому шагу.
Вкратце, Event Loop будет продолжать повторять описанные выше шаги:
-
Eveent Loop
будет продолжать перерабатыватьtasks
Самая старая задача в очереди (которую можно понимать как задачу макроса) помещается в стек для выполнения, выполняется и очищается по очереди в текущем цикле.microtask
задачи в очереди. - законченный
microtask
Задачи в очереди, естьвозможныйотобразит обновление. (Браузер очень шустрый, браузер не будет реагировать сразу на множественные изменения dom в рамках одного кадра, а будет накапливать изменения для обновления представления с частотой до 60Гц (примерно 16,7мс на кадр))
Проблема приоритета макрозадач и микрозадач
Асинхронные обратные вызовы, зарегистрированные в очереди задач (queue), делятся на два типа, макрозадачи и микрозадачи. Для удобства понимания можно считать, что в очереди задач есть очереди макрозадач и очереди микрозадач. Существует несколько очередей макрозадач и только одна микрозадача.
-
Задача макроса
- скрипт (общий код)
- setTimeout/setInterval
- setImmediate (среда узла)
- Рендеринг пользовательского интерфейса
- requestAnimationFrame
- ....
-
Микрозадача
-
Обратные вызовы в обещаниях then(), catch() и finally()
-
process.nextTick (среда узла)
-
...
-
Личное понимание порядка исполнения:
-
Код вызывает глобальный стек выполнения с самого начала выполнения, а тег скрипта выполняется как задача макроса.
-
В процессе выполнения синхронный код выполняется немедленно, а асинхронный код помещается в очередь задач.Очередь задач хранит два типа асинхронных задач: очередь макрозадач и очередь микрозадач.
-
Когда выполняется синхронный код, это означает, что выполняется первая задача макроса (скрипт)
-
1. Сначала проверьте, есть ли микрозадачи, сгенерированные во время выполнения макрозадач в очереди микрозадач в очереди задач
1-1.Если есть, очистить все микрозадачи в очереди микрозадач
2-2.Микрозадачи, сгенерированные во время выполнения микрозадач, помещаются в очередь микрозадач и освобождаются вместе во время этого выполнения
-
2. Если в очереди макросов нет задачи макроса, если есть задача макроса, то она будет выполнена, если нет, то первая волна опроса события завершится.
2-1.Сгенерированные во время выполнения микрозадачи помещаются в очередь микрозадач
2-2.После завершения задачи макроса выполните код, чтобы очистить очередь микрозадач
-
Таким образом, задача макроса получает приоритет, и все микрозадачи в очереди задач будут освобождены одновременно после выполнения задачи макроса.
процесс решения проблем
Удалить первый вопрос
// => 代码一执行就开始执行了一个宏任务-宏0
console.log('script start');
setTimeout(() => { // 宏 1
console.log('北歌');
}, 1 * 2000);
Promise.resolve()
.then(function() { // 微1-1
console.log('promise1');
})
.then(function() { // 微1-4 => 这个then中的会等待上一个then执行完成之后得到其状态才会向Queue注册状态对应的回调,假设上一个then中主动抛错且没有捕获,那就注册的是这个then中的第二个回调了。
console.log('promise2');
});
async function foo() {
await bar() // => await(promise的语法糖),会异步等待获取其返回值
// => 后面的代码可以理解为放到异步队列微任务中。 这里可以保留疑问后面会详细说
console.log('async1 end') // 微1-2
}
foo()
function bar() {
console.log('async2 end')
}
async function errorFunc () {
try {
await Promise.reject('error!!!')
} catch(e) {
// => 从这后面开始所有的代码可以理解为放到异步队列微任务中
console.log(e) // 微1-3
}
console.log('async1');
return Promise.resolve('async1 success')
}
errorFunc().then(res => console.log(res)) // 微1-5
console.log('script end');
Я не буду много говорить об использовании Promise в приведенном выше коде, позже я напишу статью об анализе исходного кода Promise.
Следует отметить одну вещь: Promise.then().then(). При регистрации асинхронной задачи обратный вызов во втором потом зависит от результата обратного вызова в первом then. Если нет исключения, асинхронная задача будет Обратный вызов, соответствующий статусу регистрации после завершения
первое исполнение
Глобально выполняется задача макроса и выводится код синхронизации. Установите Макро 1, Микро 1-1, Микро 1-2, Микро 1-3, Микро 1-4. 1 - означает, что он принадлежит к первому опросу
run: script start、 async2 end、script end
второе исполнение
После выполнения синхронного кода начинает выполняться код в очереди микрозадач в асинхронной задаче.
Очередь микрозадач: есть только одна очередь, и она будет очищена один раз в текущем опросе.
run:
执行微1-1: promise1
执行微1-2: async1 end
执行微1-3: error!!!、async1 。当前异步回调执行完毕才Promise.resolve('async1 success'),然后注册then()中的成功的回调-微1-5
执行微1-4: promise2
执行刚刚注册的微1-5: async1 success
В конце этой первой волны опроса
третья казнь
Включить вторую волну опроса: выполнить макрос 1
run: '北歌'
Подойди сюда. Все голосование заканчивается.
На самом деле разобраться в микрозадаче относительно сложно.Для микрозадачи, то есть только одна очередь будет в вышеупомянутомв этот разОчистить один раз во время опроса (включаяв этот размикрозадачи, генерируемые во время выполнения).
Возьми каштан 🌰
Вы идете в столовую и стоите в очереди, чтобы заказать еду.Изначально вы планировали сегодня съесть только два блюда (в очереди микрозадач зарегистрировано только два колбэка).В процессе приготовления вы видите свою любимую тушеную свинину ( выполняется выполнение микрозадачи) в процессе встречаются новые микрозадачи), необходимо добавить еще одно блюдо (добавить микрозадачи в очередь микрозадач)
анализ чертежей
Я считаю, что вы все должны понять из приведенного выше объяснения.Чтобы дать вам более глубокое понимание и сформировать сильное впечатление от сцены, я нарисовал движущуюся картинку
Для всеобщего удобства я разместил код
Дагуай Продвинутый
Благодаря приведенному выше объяснению, теперь вы можете почистить несколько вопросов, чтобы увидеть, насколько хорошо вы держите. (Обновление: парсинг будет немного длинным, вы можете написать его самостоятельно, а затем прочитать для лучшего понимания)
РегулированиеТеперь для вашего понимания, пожалуйста, запомните правила:
- Анализ основан на каждом опросе, и блок кода синхронизации напрямую выводит результат
- В блоке кода асинхронной задачи красный цвет представляет макрозадачи, а зеленый — микрозадачи.
-
微1-
представляет все микрозадачи в очереди микрозадач в первом опросе,微2-
значит второй раз и так далее
золотой вопрос
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
})
new Promise((resolve) => {
console.log('4');
resolve();
}).then(() => {
console.log('5')
})
})
Promise.reject().then(() => {
console.log('13');
}, () => {
console.log('12');
})
new Promise((resolve) => {
console.log('7');
resolve();
}).then(() => {
console.log('8')
})
setTimeout(() => {
console.log('9');
Promise.resolve().then(() => {
console.log('10');
})
new Promise((resolve) => {
console.log('11');
resolve();
}).then(() => {
console.log('12')
})
})
Этот вопрос относительно прост для анализа.
каменная кладка
new Promise((resolve, reject) => {
console.log(1)
resolve()
})
.then(() => { // 微1-1
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => { // 宏2
reject();
}, 3 * 1000);
resolve() // TODO 注1
})
.then(() => { // 微1-2 TODO 注2
console.log(4)
new Promise((resolve, reject) => {
console.log(5)
resolve();
})
.then(() => { // 微1-4
console.log(7)
})
.then(() => { // 微1-6
console.log(9)
})
})
.then(() => { // 微1-5 TODO 注3
console.log(8)
})
})
.then(() => { // 微1-3
console.log(6)
})
=
Разобрать:
первый опрос
выполнение тега скрипта (макрос 0)
Выходной код синхронизации:
1
Монтировать асинхронные задачи:
() => { // 微1-1 -> 在该回调还没有被放到主栈中执行的时候里面都是普通的代码你也不知道里面那些是异步任务
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => {
reject();
}, 3 * 1000);
resolve()
})
.then(() => {
console.log(4)
new Promise((resolve, reject) => {
console.log(5)
resolve();
})
.then(() => {
console.log(7)
})
.then(() => {
console.log(9)
})
})
.then(() => {
console.log(8)
})
}
Когда код синхронизации завершается, также выполняется первая макрозадача, и очередь микрозадач в асинхронной задаче начинает очищаться:
微1-1: 2、3
Примечание 1
После того, как состояние промиса изменено, его нельзя изменить (активная ошибка все еще может повлиять на состояние следующего тогда), а отклоненное не было выполнено в задаче макроса, поэтому новое состояние промиса в Micro 1- 1 выполняется.
Заметка 2
После того, как newPromise в Micro 1-1 определит состояние, первый callback в первом потом может быть прописан в асинхронной задаче, но второй не прописан, и после того, как Micro 1-2 смонтирован, micro 1- 1 выполнение обратного вызова не имеет исключения задачи, и состояние продолжает зависать на самом внешнем тогда (1-3)
Новые микрозадачи, сгенерированные при выполнении микрозадач, помещаются в очередь микрозадач, и при этом опросе микрозадачи также очищаются:
() => { // 微1-2
console.log(4)
new Promise((resolve, reject) => {
console.log(5)
resolve();
})
.then(() => {
console.log(7)
})
.then(() => {
console.log(9)
})
}
) => { // 微1-3
console.log(6)
}
Макрозадачи, сгенерированные во время выполнения микрозадач, помещаются в новую очередь макрозадач:
() => { // 宏2
reject();
}
Начать выполнение микрозадач, сгенерированных во время выполнения микрозадачи:
- Сначала выполните Микро 1-2
微1-2: 4、5
Выполнение микрозадач1-2Новые микрозадачи, сгенерированные в опросе, будут помещены в очередь микрозадач, и микрозадачи также будут освобождены в этом опросе, но вам нужно дождаться завершения выполнения предыдущих микрозадач (это хорошая иллюстрация очереди).первым пришел-первым вышел):
() => { // 微1-4
console.log(7)
}
() => { // 微1-5
console.log(8)
}
Заметка 2
или держись за этоМикро 1-3так же, вМикро-2После выполнения шага разрешения() можно определить статус первого обратного вызова в первом .затем можно определить следующее, зарегистрироватьсяМикро 1-4но второй еще не завис, потому что указанный выше обратный вызов еще не был выполнен (можете ли вы быть уверены, что в приведенном выше случае нет активной ошибки? Таким образом, второй тогда может не обязательно регистрировать успешный обратный вызов), это выигрываетМикро 1-2Выполнение завершено, и состояние продолжается вниз.Микро 1-2Затем ниже вы можете зарегистрировать успешный обратный вызов, зарегистрироватьМикро 1-5
- Выполнить Микро 1-3
微1-3: 6
Микро 1-3Может быть выполнен после выполненияМикро 1-2Микрозадачи, генерируемые во время выполнения
- Выполнить Микро 1-4
微1-4: 7
Выполнение микрозадач1-4Новые микрозадачи, сгенерированные в опросе, будут помещены в очередь микрозадач, и микрозадачи также будут освобождены в этом опросе, но вам нужно дождаться завершения выполнения предыдущих микрозадач (это хорошая иллюстрация очереди).первым пришел-первым вышел):
() => { // 微1-6
console.log(9)
}
- Выполнить Микро 1-5
微1-5: 8
Микро 1-5Может быть выполнен после выполненияМикро 1-4Микрозадачи, генерируемые во время выполнения
微1-6: 9
На данный момент микрозадача окончательно опустошена, и выходные результаты в конце этого опроса следующие:
同步代码: 1
微1-1: 2、3
微1-2: 4、5
微1-3: 6
微1-4: 7
微1-5: 8
微1-6: 9
второй опрос
Откройте макрос 2, этот код
() => { // 宏2
reject();
}
то состояние уже определено, и этот код не молоток.
Выходной результат:
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
Вопрос короля
Promise.resolve()
.then(() => {
console.log('promise1');
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('timer2')
resolve()
}, 0)
})
.then(async () => {
await foo();
return new Error('error1')
})
.then((ret) => {
setTimeout(() => {
console.log(ret);
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then(res => {
console.log("then: ", res)
})
.catch(err => {
console.log("catch: ", err)
})
}, 1 * 3000)
}, err => {
console.log(err);
})
.finally((res) => {
console.log(res);
throw new Error('error2')
})
.then((res) => {
console.log(res);
}, err => {
console.log(err);
})
})
.then(() => {
console.log('promise2');
})
function foo() {
setTimeout(() => {
console.log('async1');
}, 2 * 1000);
}
setTimeout(() => {
console.log('timer1')
Promise.resolve()
.then(() => {
console.log('promise3')
})
}, 0)
console.log('start');
Этот вопрос похож на самый сильный королевский вопрос. Сначала я проанализирую его таким образом. Следующий вопрос имеет каждый шаг анализа, иначе количество слов будет превышено (рекомендуется анализировать его в среде IDE):
Promise.resolve()
.then(() => { // 微1-1
console.log('promise1');
return new Promise((resolve, reject) => {
setTimeout(() => { // 宏3
console.log('timer2')
resolve() // TODO 因为是在异步确认Promise的状态,
// TODO 所以当状态还没有确定之前下面的then中的回调都不会注册当异步任务中
}, 0)
})
.then(async () => { // 微3-1
// TODO 先看着
// await 表达式简单理解为'异步等待'获取结果,
// await是为了优化'Promise'的then链写法 直接帮你去异步等待拿到结果
// 可以理解为 await foo() => Promise.then((res) => res)
// await表达式后面的代码可以看成 Promise.then((res) => res).then(() => {return new Error('error1')})
console.log( await foo()) // TODO 注: 对于await解释下面第四天题的分析很透彻了,不知道可以往下看
// TODO 再看着
// await 表达式需要异步去等待获取,await表达式下面的代码相当于挂在到异步队列微任务中
// 但前提是需要异步等待获取结果之后
return new Error('error1') // 微4-1
})
.then((ret) => { // 微4-2
setTimeout(() => { // 宏5
console.log(ret);
Promise.resolve()
.then(() => { // 微5-1
return new Error('error!!!')
})
.then(res => { // 微5-2
console.log("then: ", res)
})
.catch(err => {
console.log("catch: ", err)
})
}, 1 * 3000)
}, err => {
console.log(err);
})
.finally((res) => { // 微3-2 前面状态不确定,但是finally不管状态如何都执行且不接受任务参数
console.log(res);
throw new Error('error2')
})
.then((res) => { // 微3-3
console.log(res);
}, err => {
console.log(err);
})
})
.then(() => { // 微3-4
console.log('promise2');
})
async function foo() {
setTimeout(() => { // 宏4
console.log('async1');
}, 2 * 1000);
return Promise.resolve(1)
}
setTimeout(() => { // 宏2
console.log('timer1')
Promise.resolve()
.then(() => { // 微2-1
console.log('promise3')
})
}, 0)
console.log('start');
/**
* TODO 微1-表示第一次轮询中的微任务
*
* 第一次轮询
* 代码首次加载script作用宏任务执行:
* 挂载异步任务: 微1-1 宏2
* 输出: start
*
* 宏任务执行完毕: 开始执行微任务列表, 微1-1
* 挂载异步任务: 宏3
* 输出: promise1
*
*
*
* 第二次轮询
* 首先执行宏任务: 宏2
* 挂载异步任务: 微2-1
* 输出: timer1
*
* 宏任务执行完毕: 开始执行微任务列表, 微2-1
* 挂载异步任务: 无
* 输出: promise3
*
*
*
*
* 第三次轮询
* 首先执行宏任务: 宏3
* 挂载异步任务: 微3-1 微3-2 微3-3 微3-4
* 输出: timer2
*
* 宏任务执行完毕: 开始执行微任务列表, 微3-1 微3-2 微3-3 微3-4
* 挂载异步任务: 宏4
* 输出: undefined error2 promise2
*
*
*
* 第四次轮询
* 首先执行宏任务: 宏4
* 挂载异步任务: 微4-1
* 输出: async1
*
* 宏任务执行完毕: 开始执行微任务列表, 微4-1
* 挂载异步任务: 微4-2
* 输出: 没有输出, 现在的回调在没有确定状态都注册过且在轮询中被调用过, 很好的说明了挂在异步时候的是callBack
*
*
* 第五次轮询
* 首先执行宏任务: 宏5
* 挂载异步任务: 微4-1
* 输出: error1 then: error1!!!
*/
// start ->promise1 -> timer1 -> promise3 -> timer2 -> undefined -> error2 ->
// promise2 -> async1 -> error1 -> then: error!!!
Король славы
Давайте ответим на этот последний вопрос вместе.
async function async1() {
console.log('async1 start');
new Promise((resolve, reject) => {
try {
throw new Error('error1')
} catch(e) {
console.log(e);
}
setTimeout(() => { // 宏3
resolve('promise4')
}, 3 * 1000);
})
.then((res) => { // 微3-1
console.log(res);
}, err => {
console.log(err);
})
.finally(res => { // 微3-2 // TODO注3
console.log(res);
})
console.log(await async2()); // 微4-1 TODO-注1
console.log('async1 end'); // 微4-2 // TODO-注2
}
function async2() {
console.log('async2');
return new Promise((resolve) => {
setTimeout(() => { // 宏4
resolve(2)
}, 1 * 3000);
})
}
console.log('script start');
setTimeout(() => { // 宏2
console.log('setTimeout');
}, 0)
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
})
.then(() => { // 微1-2
console.log('promise2');
return new Promise((resolve) => {
resolve()
})
.then(() => { // 微1-3
console.log('then 1-1')
})
})
.then(() => { // 微1-4
console.log('promise3');
})
console.log('script end');
Регулирование
Теперь для вашего понимания, пожалуйста, запомните правила:
- Анализ основан на каждом опросе, и блок кода синхронизации напрямую выводит результат
- В блоке кода асинхронной задачи красный цвет представляет макрозадачи, а зеленый — микрозадачи.
-
微1-
представляет все микрозадачи в очереди микрозадач в первом опросе,微2-
значит второй раз и так далее
первый опрос
выполнение тега скрипта (макрос 0)
Выходной код синхронизации:
script start -> async1 start -> error1 -> async2 -> promise1 -> script end
Монтировать асинхронные задачи:
-() => { // 宏2
- console.log('setTimeout');
-}
-() => { // 宏3
- resolve('promise4')
-}
-() => { // 宏4
- resolve(2)
-}
+() => { // 微1-1
+ console.log('promise2');
+ return new Promise((resolve) => {
+ resolve()
+}
Когда код синхронизации завершается, также выполняется первая макрозадача, и очередь микрозадач в асинхронной задаче начинает очищаться:
微1-1: promise2 -> 微1-2: then 1-1 -> 微1-3: promise3
Новые микрозадачи, сгенерированные при выполнении микрозадач, помещаются в очередь микрозадач, и при этом опросе микрозадачи также очищаются:
+() => { // 微1-2
+ console.log('then 1-1')
+}
+() => { // 微1-3
+ console.log('promise3');
+}
Макрозадачи, сгенерированные во время выполнения микрозадач, помещаются в новую очередь макрозадач:
本次微任务执行没有产生新的宏任务
Примечание 1
Здесь я должен сказать, что многие думают, что ожидание означает синхронизировать код.На самом деле ожидание - это синтаксический сахар промиса, и внутренняя реализация также опирается на промис.Он родился для оптимизации тогдашней цепочки написания промисов и записи асинхронный код синхронным образом, чтобы код выглядел более кратким и ясным. Настоящее значение await — это асинхронное ожидание (имеется в виду асинхронное ожидание). Выражение await эквивалентно вызову метода then, возвращающего обещание, и асинхронному (ожидание ) получает возвращаемое значение. то есть ожиданиеобещание.затем
Задача макроса открывается в промисе, возвращаемом функцией async2 здесь, и ожидание асинхронного ожидания должно дождаться выполнения задачи макроса, чтобы получить возвращаемое значение, что означает, что задача макроса не может вызвать метод then обещания в все без выполнения выражения ожидания.
Заметка 2
Как упоминалось ранее, код, стоящий за выражением ожидания, может быть просто понят как помещенный в микрозадачу, но предыдущее выражение ожидания вообще не получает результат асинхронного ожидания.В задаче в некоторых руководствах говорится, что код ожидания выражение может считаться первым в очереди микрозадач Это утверждение неверно!
В конце этого опроса выходные результаты таковы:
script start -> async1 start -> error1 -> async2 -> promise1 -> script end
微1-1: promise2 -> 微1-2: then 1-1 -> 微1-3: promise3
второй опрос
Первая волна опроса выше завершена, и начинается вторая волна опроса
Выполните вторую очередь задач макроса (в очереди задач макроса хранится только одна задача макроса):
宏2:setTimeout
После выполнения макрозадачи новая микрозадача и новая макрозадача не создаются. Конец второго опроса
В конце этого опроса выходные результаты таковы:
宏2:setTimeout
третий опрос
Выполните третью очередь задач макроса (в очереди задач макроса хранится только одна задача макроса):
宏任务本身没有输出啥,不过确定了下Promise的状态并传递了个'promise4'给下一个then中的成功回调
Новые микрозадачи, сгенерированные во время выполнения макрозадач, помещаются в очередь микрозадач:
+(res) => { // 微3-1
+ console.log(res);
+}
После выполнения макрозадачи очередь микрозадач в асинхронной задаче очищается:
微3-1: promise4 -> 微3-2: undefined
Новые микрозадачи, сгенерированные при выполнении микрозадач, помещаются в очередь микрозадач, и при этом опросе микрозадачи также очищаются:
+res => { // 微3-2 // TODO注3
+ console.log(res);
+}
Макрозадачи, сгенерированные во время выполнения микрозадач, помещаются в новую очередь макрозадач:
本次微任务执行没有产生新的宏任务
В конце этого опроса выходные результаты таковы:
微3-1: promise4 -> 微3-2: undefined
Заметка 3
Как упоминалось ранее, promise.finally() также является микрозадачей и, наконец, может пониматься как выполнение меня независимо от того, является ли состояние обещания успешным или неудачным. Но я не приемлю никакого результата. Итак, наконец, не могу принять возвращаемое значение res undefined
Четвертый опрос
Выполните четвертую очередь задач макроса (в очереди задач макроса хранится только задача макроса):
宏任务本身没有输出啥,不过确定了下Promise的状态并传递了个2给下一个then中的成功回调
Новые микрозадачи, сгенерированные во время выполнения макрозадач, помещаются в очередь микрозадач:
Как упоминалось выше, await => Promise.then(), выполнение вышеуказанной задачи макроса подтверждает, что состояние обещания может быть использовано для получения результата асинхронного ожидания. Эквивалентно этому: Promise.then((res) => {return res}) И код после выражения ожидания эквивалентенПосле асинхронного ожидания результатапоставить в очередь микрозадач Эквивалентно этому: Promise.then((res) => {return res}).finally(() => {}), только после получения результата перед выражением await код зависнет в асинхронной очереди
Вы можете провести эксперимент, чтобы установить значение вышеуказанного таймера асинхронного ожидания на более длительное время. В настоящее время код, стоящий за выражением ожидания, не отвечает. Он будет реагировать только тогда, когда будет получен результат асинхронного ожидания.
+(res) => {return res} // 微4-1
+() => {async1 end} // 微4-2
После выполнения макрозадачи очередь микрозадач в асинхронной задаче очищается:
微4-1: 2 -> 微4-2: async1 end
Новые микрозадачи, сгенерированные при выполнении микрозадач, помещаются в очередь микрозадач, и при этом опросе микрозадачи также очищаются:
本次微任务执行没有产生新的微任务
Макрозадачи, сгенерированные во время выполнения микрозадач, помещаются в новую очередь макрозадач:
本次微任务执行没有产生新的宏任务
В конце этого опроса выходные результаты таковы:
微4-1: 2 -> 微4-2: async1 end
Полные результаты после завершения всех опросов
script start -> async1 start -> error1 -> async2 -> promise1 -> script end
微1-1: promise2 -> 微1-2: then 1-1 -> 微1-3: promise3
宏2:setTimeout
微3-1: promise4 -> 微3-2: undefined
微4-1: 2 -> 微4-2: async1 end
Если вы ответили на все четыре вопроса правильно, поздравляю.
напиши в конце
Я очень серьезно отношусь к этой серии статей о [Front-end System], и я действительно хочу написать хорошо, но, в конце концов, я все еще новичок во front-end и начинаю писать. не очень хорошо написано или есть вопросы, добро пожаловать Все отметили, что я буду продолжать пересматривать следующие статьи. Я также надеюсь, что смогу расти вместе с вами по мере моего прогресса. Друзья, которым нравятся мои статьи, также могут обратить внимание
Я был бы признателен, если бы первым подписался на меня.В это время молодые я и ты шли в бой налегке, потом богатые ты и я возвращались с полным грузом.
серия статей
[Фронтальная система] Построить многоэтажку с фундамента
[Фронтальная система] Сценарий приложения регулярного в разработке — это не просто проверка правил