Во-первых, проблема однопоточного js
js является однопоточным, и задачи обработки обрабатываются одна за другой, поэтому, если задачу нужно обрабатывать долго, последующие задачи будут заблокированы.
Таким образом, js решает эту проблему с помощью цикла событий.Прежде чем понять цикл событий, нам нужно понять некоторые ключевые слова.
Во-вторых, что такое стек, очередь, куча, цикл событий
- стек (стек): съесть слишком много и вырвать
- очередь (очередь): съешь слишком много... отпусти
- куча (куча): хранить объекты obj
стек выполнения
Когда js-движок работает, когда код начинает работать, он помещает код в стек выполнения для выполнения.
пример:
Когда код анализируется, функции помещаются в стек по очереди.
Если есть толчок в стек, то должно быть и выталкивание.При выполнении функции c она начинает выталкиваться из стека.
Когда стек выполнения сталкивается с асинхронностью
Стек фронтального выполнения сначала входит, а затем выходит, но он также является синхронным Синхронизация означает блокировку, поэтому он должен быть асинхронным Что происходит, когда асинхронный код появляется в стеке выполнения
На этом этапе кода добавьте событие клика и setTimeout, а теперь наблюдайте за порядком выполнения.
Наблюдайте за эффектом стека выполнения в это время, который значительно отличается от вложенности функций выше.
1. Оператор console.log("sync") не будет перемещен в конец стека выполнения, поскольку консоль завершила выполнение.
2. И click, и settimeout помещаются в стек, но их внутренние консоли не помещаются в стек, а значит, они не выполнялись.
3. Если щелчок не выполняется, почему setTimeout толкает стек, не должен ли он быть заблокирован?
Ответ таков: когда браузер выполняет стек выполнения и обнаруживает, что есть асинхронные задачи, он будет передан на обслуживание webapi, а стек выполнения продолжит выполнение следующих задач.
Аналогично, Settimeout также будет добавлен в webapi
webapi — это функция, реализованная самим браузером, и здесь специально поддерживаются события.
Рядом с setTimeout выше есть индикатор выполнения, и этот прогресс представляет собой установленное время ожидания.
очередь обратного вызова
В приведенном выше примере, когда выполнение setTimeout заканчивается, следует ли ему вернуться в стек выполнения и выполнить вывод?
Ответ: Нет!
В этот момент исполняемая функция setTimeout после завершения обратного отсчета помещается в очередь обратного вызова.
Наконец, исполняемая функция setTimeout извлекается из очереди обратного вызова и снова помещается в стек выполнения.
Такой процесс выполнения называется циклом событий циклом событий.
Конкретный процесс цикла событий
После того, как задача стека выполнения будет очищена, задача будет взята из головы очереди обратного вызова.
Выше приведен самый простой пример, результат вывода 1, 3, 2
почему это?
На приведенном выше рисунке показана конкретная последовательность выполнения:
1, console.log(1) помещается в стек выполнения.
2 setTimeout распознается как асинхронная задача в стеке выполнения и помещается в webapis
3. Файл console.log(3) помещается в стек выполнения.В это время исполняемый код setTimeout все еще ожидает в очереди обратного вызова.
4. После выполнения console.log(3) возьмите console.log(2) из головы очереди обратного вызова и поместите его в стек выполнения.
5, выполняется console.log(2)
Очередь обратного вызова в порядке очереди
Важно отметить, что очередь обратного вызова находится в порядке поступления, например:
При выполнении console.log(4) console.log(2) удаляется из очереди обратного вызова;
Примечание: console.log(3) будет извлечен из очереди обратного вызова только после завершения выполнения console.log(2) и повторной очистки стека выполнения.
Проверьте правильность концепции
Приведенный выше код, наконец, выводит 1, 5, 2, 4, 3, процесс выполнения:
1, вывод 1, поместить 2 в очередь обратного вызова
2. Поместите 4push в очередь обратного вызова
3, выход 5
4. Очистите стек выполнения, прочитайте выход 2, найдите 3, нажмите 3 в очередь обратного вызова
5, стек выполнения пуст, вывод чтения 4
6, очистить стек выполнения, прочитать вывод 3
Пока вроде все ок, но! ! ! ! ! ! , это еще не конец
Macrotask (макрозадача), Microtask (микрозадача)
В приведенном выше примере вы должны иметь определенное представление о цикле событий, а теперь продолжайте смотреть на пример.
console.log(1);
setTimeout(()=>{
console.log(2)
})
var p = new Promise((resolve,reject)=>{
console.log(3)
resolve("成功")
})
p.then(()=>{
console.log(4)
})
console.log(5)
Согласно концепции событийного цикла, это должно быть 1, 3, 5, 2, 4, потому что setTimeout и затем будет помещен в очередь обратного вызова, а затем он сначала вошел, первый вышел, поэтому сначала должно быть 2 вывода, 4 выход после
Но на самом деле порядок вывода 1, 3, 5, 4, 2!
Это связано с тем, что метод обещания then считается находящимся в очереди микрозадач Microtask.
Что такое макрозадача
Macrotask (макрозадача) хорошо понятна, это очередь обратного вызова, которую мы представили ранее
Что такое микрозадача
Микрозадача — это тоже очередь задач,Порядок выполнения этой очереди после очистки стека выполнения
Это показано с картинками
Вы можете видеть, что макрозадача (макрозадача) также является микрозадачей (микрозадачей) в очереди обратного вызова.
Хотя Микрозадача — это очередь,Но не по одному в стек выполнения, но когда стек выполнения пуст, все задачи в очереди микрозадач будут выполнены, и, наконец, будет получена первая макрозадача (макрозадача) из очереди обратного вызова.
пример:
Процесс выполнения выше:
1. Дайте setTimeout для отправки в задачу макроса
2. Вставьте затем(2) в микрозадачу
3. Вставьте затем(4) в микрозадачу
4. Очередь задач пуста, возьмите первую затем(2) микрозадачу и поместите ее в стек выполнения.
5. Выход 2, вставить затем(3) в микрозадачу
6. Очередь задач пуста, возьмите первую микрозадачу then(4) и поместите ее в стек выполнения.
7, выход 4
8. Очередь задач пуста, возьмите первую затем(3) микрозадачу и поместите ее в стек выполнения.
9, выход 3
10. Очередь задач пуста, и микрозадача тоже пуста, выньте setTimeout(1) в макрозадаче
11, выход 1
Зачем тогда микрозадача
Это связано с каждым браузером. Обещания, реализованные каждым браузером, разные. Некоторые из них являются макрозадачами, некоторые — микрозадачами, а Chrome — микрозадачами. Как правило, они являются микрозадачами по умолчанию.
В дополнение к этому, несколько событий также считаются микрозадачами:
- process.nextTick
- promises
- Object.observe
- MutationObserver
console.log("start");
setImmediate(()=>{
console.log(1)
})
Promise.resolve().then(()=>{
console.log(4);
})
Promise.resolve().then(()=>{
console.log(5);
})
process.nextTick(function foo() {
console.log(2);
});
process.nextTick(function foo() {
console.log(3);
});
console.log("end")
Приведенный выше код выводит start, end, 2, 3, 4, 5, 1.
Концепция Process.nextTick и THEN не так уж и велики, process.nextTick добавляется в конец стека выполнения, поэтому и другие показатели несовместимы.
финальный тест
console.log("1");
setTimeout(()=>{
console.log(2)
Promise.resolve().then(()=>{
console.log(3);
process.nextTick(function foo() {
console.log(4);
});
})
})
Promise.resolve().then(()=>{
console.log(5);
setTimeout(()=>{
console.log(6)
})
Promise.resolve().then(()=>{
console.log(7);
})
})
process.nextTick(function foo() {
console.log(8);
process.nextTick(function foo() {
console.log(9);
});
});
console.log("10")
Порядок выполнения:
1, выход 1
2. Вставьте setTimeout(2) в задачу макроса.
3. Вставьте затем(5) в микрозадачу
4. Добавьте nextTick(8) в конец стека выполнения.
5, выход 10
6. Выполнить следующий тик (8)
7, выход 8
8. Добавьте nextTick(9) в конец стека выполнения
9, выход 9
10. Затем выполните микрозадачи (5)
11, выход 5
12. Вставьте setTimeout(6) в задачу макроса.
13. Вставьте затем(7) в микрозадачу
14. Затем выполняйте микрозадачи (7)
15, выход 7
16, выньте setTimeout(2)
17, выход 2
18. Вставьте затем(3) в микрозадачу
19. Затем выполняйте микрозадачи (3)
20, выход 3
21. Добавьте nextTick(4) в конец стека выполнения
22, выход 4
23, выньте setTimeout(6)
24, выход 6
Конечный результат: 1, 10, 8, 9, 5, 7, 2, 3, 4, 6