Эта статья участвовала в "Проект «Звезда раскопок»”, чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.
предисловие
Всем привет, меня зовут Линь Сансинь, оEventLoopОчков знаний в тесте в обычное время много, но они также тесно связаны с нашей повседневной работой.EventLoopПорядок выполнения может очень помочь нам найти проблему. На самом деле нормальноEventLoopПорядок легко сказать, но еслиsetTimeout + Promise + async/awaitОбъединить усилия очень сложно. Сегодня я возьму тебя过五关斩六将, победить их! ! !
Примечание. В этой статье не рассматривается механизм выполнения Nodejs.
Синхронный Асинхронный
Что такое асинхронный и что синхронный, много говорить не буду, расскажу через небольшую историю.
-
同步: Вы звоните в книжный магазин, чтобы заказать книги, начальник говорит я проверю, вы не вешаете трубку и ждете, начальник скажет вам результаты, вы не можете заниматься своими делами в этот период -
异步: Ты звонишь в книжный магазин, чтобы заказать книги, начальник сказал, я посмотрю, я тебе потом скажу, ты кладешь трубку и иди сначала занимайся своими делами.
Механизм выполнения JS
На самом деле это не сложно, механизм выполнения кода JavaScript, я сведу его к трем предложениям
- 1. встреча
同步代码прямое исполнение - 2. встреча
异步代码отложи это и положи его回调函数сохранить его, сохранить его事件队列 - 3. Ждать всех
同步代码все выполняются, а затем из事件队列все хранится в异步回调函数Выньте его и выполните по порядку
См. пример ниже
console.log(1) // 同步
setTimeout(() => {
console.log(2) // 异步
}, 2000);
console.log(3) // 同步
setTimeout(() => {
console.log(4) // 异步
}, 0);
console.log(5) // 同步
输出 : 1 3 5 4 2
Макрозадачи и микрозадачи
Как упоминалось ранее, после выполнения всего кода синхронизации начните с事件队列выполнять все подряд异步回调函数.
фактически事件队列Это тоже небольшая группа, а у других свои правила, это похоже на школу, которая управляет многими клубами, и у их собственных клубов тоже свои правила.
Опять же, почему事件队列Вам нужны свои правила? В противном случае, вы должны сначала подумать, почему клубы в школе имеют свои правила и оценки.Это потому, что у одних людей сильные способности, а у других слабые, поэтому есть уровни уровней. фактически事件队列тоже самое,事件队列Он используется для хранения асинхронных обратных вызовов, но асинхронные также делятся на типы.Асинхронные задачи делятся на宏任务а также微任务,а такжеМикрозадачи выполняются раньше макрозадач
Что такое макрозадачи и микрозадачи?
задача макроса
| # | браузер | Node |
|---|---|---|
| I/O | ✅ | ✅ |
| setTimeout | ✅ | ✅ |
| setInterval | ✅ | ✅ |
| setImmediate | ❌ | ✅ |
| requestAnimationFrame | ✅ | ❌ |
микрозадачи
| # | браузер | Node |
|---|---|---|
| Promise.prototype.then catch finally | ✅ | ✅ |
| process.nextTick | ❌ | ✅ |
| MutationObserver | ✅ | ❌ |
Процесс реализации
Итак, давайте поговорим об общем процессе выполнения.
пример
Вы можете следовать моим шагам по решению проблем, в основном 90% проблем не вызывают стресса! ! !
- 1. Маркировка различия между асинхронным и синхронным
- 2. В асинхронности маркеры различают макрозадачи и микрозадачи
- 3. Количество раундов, медленно идите круг за кругом
console.log(1) // 同步
setTimeout(() => {
console.log(2) // 异步:宏任务
});
console.log(3) // 同步
Promise.resolve().then(()=>{ // 异步:微任务
console.log(4)
})
console.log(5) // 同步
первый раунд
- Описание: сначала выведите синхронное выполнение
- Выход: 1, 3, 5
- Создайте задачу макроса:
setTimeout, который производит микрозадачи:Promise.prototype.then
второй раунд
- Описание: микрозадачи выполняются в первую очередь
- выход: 4
- Макрозадача создания: нет, микрозадача создания: нет
- Оставшиеся задачи макроса:
setTimeout, остальные микрозадачи: нет
Третий тур (конец)
- Описание: выполнить задачу макроса
- выход: 2
- Макрозадача создания: нет, микрозадача создания: нет
- Оставшиеся макрозадачи: нет, оставшиеся микрозадачи: нет
первый раунд
Подумайте об идее решения проблемы, которую я только что упомянул. Вы можете следовать этой идее. Эта проблема — вопрос нескольких минут.
console.log(1)
setTimeout(() => {
console.log(2)
Promise.resolve().then(() => {
console.log(3)
})
});
console.log(4)
new Promise((resolve,reject) => {
console.log(5)
resolve()
}).then(() => {
console.log(6)
setTimeout(() => {
console.log(7)
})
})
console.log(8)
Шаг 1: Марк
Примечание: обещания
executorЭто синхронно! ! !
console.log(1) // 同步
setTimeout(() => {
console.log(2) // 异步:宏任务 setTimeout1
Promise.resolve().then(() => { // 异步:微任务 then1
console.log(3)
})
});
console.log(4) // 同步
new Promise((resolve,reject) => {
console.log(5) // 同步
resolve()
}).then(() => { // 异步:微任务 then2
console.log(6)
setTimeout(() => {
console.log(7) // 异步:宏任务 setTimeout2
})
})
console.log(8) // 同步
Шаг 2: Разделите раунды
| количество раундов | иллюстрировать | выход | производить | остальной |
|---|---|---|---|---|
| первый раунд | Выполнить вывод внешней синхронизации | 1, 4, 5, 8 | Задача макроса:setTimeout1Микрозадачи: then2
|
Задача макроса:setTimeout1Микрозадачи: then2
|
| второй раунд | Выполнение микрозадачthen2
|
6 | Задача макроса:setTimeout2Микрозадачи: нет |
Задача макроса:setTimeout1,setTimeout2Микрозадачи: нет |
| третий раунд | Выполнять задачи макросаsetTimeout1
|
2 | Задача макроса: нет Микрозадачи: then1
|
Задача макроса:setTimeout2Микрозадачи: then1
|
| четвертый раунд | Выполнение микрозадачthen1
|
3 | Задача макроса: нет Микрозадачи: нет |
Задача макроса:setTimeout2Микрозадачи: нет |
| пятый раунд | Выполнять задачи макросаsetTimeout2
|
7 | Задача макроса: нет Микрозадачи: нет |
Задача макроса: нет Микрозадачи: нет |
Второе препятствие
все встречаютсяPromise.then.thenВ это время, если вы немного запутались, вы можете переключить его, и мы поговорим об этом ниже.
Уведомление:
thenметод автоматически вернет новыйPromise, то есть,return new Promise,специфическийPromise源码Каждый может видеть меня этой статьей.После прочтения принцип написания промиса от руки, самая понятная версия[Чтение: 1,1 Вт, лайков: 430]
setTimeout(() => {
console.log(1)
}, 0)
console.log(2)
const p = new Promise((resolve) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
}).then(() => {
console.log(5)
})
console.log(6)
Шаг 1: Отметить + Преобразовать
Примечание. Преобразование здесь только для вопроса, его легче понять, обычно не делайте этого преобразования, обычно это преобразование не подходит, это неправильно.
setTimeout(() => { // 异步:宏任务 setTimeout
console.log(1)
}, 0)
console.log(2) // 同步
const p = new Promise((resolve) => { // p 是 then1 执行返回的新 Promise
console.log(3) // 同步
resolve()
}).then(() => { // 异步:微任务 then1
console.log(4)
// 拿着 p 重新 then
p.then(() => { // 异步:微任务 then2
console.log(5)
})
})
console.log(6) // 同步 6
Шаг 2: Разделите раунды
| количество раундов | иллюстрировать | выход | производить | остальной |
|---|---|---|---|---|
| первый раунд | Выполнить синхронный вывод | 2, 3, 6 | Задача макроса:setTimeoutМикрозадачи: then1
|
Задача макроса:setTimeoutМикрозадачи: then1
|
| второй раунд | Выполнение микрозадачthen1
|
4 | Задача макроса: нет Микрозадачи: then2
|
Задача макроса:setTimeoutМикрозадачи: then2
|
| третий раунд | Выполнение микрозадачthen2
|
5 | Задача макроса: нет Микрозадачи: нет |
Задача макроса:setTimeoutМикрозадачи: нет |
| четвертый раунд | Выполнять задачи макросаsetTimeout
|
1 | Задача макроса: нет Микрозадачи: нет |
Задача макроса: нет Микрозадачи: нет |
Третий уровень
Скажи еще раз: все сталкиваютсяPromise.then.thenВ это время, если вы немного запутались, вы можете изменить его
Уведомление:
thenметод автоматически вернет новыйPromise, то есть,return new Promise,специфическийPromise源码, вы можете увидеть мою статьюПосле прочтения принцип написания промиса от руки, самая понятная версия[Чтение: 1,1 Вт, лайков: 430]
new Promise((resolve,reject)=>{
console.log(1)
resolve()
}).then(()=>{
console.log(2)
new Promise((resolve,reject)=>{
console.log(3)
resolve()
}).then(()=>{
console.log(4)
}).then(()=>{
console.log(5)
})
}).then(()=>{
console.log(6)
})
Шаг 1: Отметить + Преобразовать
Примечание. Преобразование здесь только для вопроса, его легче понять, обычно не делайте этого преобразования, обычно это преобразование не подходит, это неправильно.
const p1 = new Promise((resolve, reject) => { // p1 是 then1 执行返回的新 Promise
console.log(1) // 同步
resolve()
}).then(() => { // 异步:微任务 then1
console.log(2)
const p2 = new Promise((resolve, reject) => { // p2 是 then2 执行返回的新 Promise
console.log(3) // then1 里的 同步
resolve()
}).then(() => { // 异步:微任务 then2
console.log(4)
// 拿着 p2 重新 then
p2.then(() => { // 异步:微任务 then3
console.log(5)
})
})
// 拿着 p1 重新 then
p1.then(() => { // 异步:微任务 then4
console.log(6)
})
})
Шаг 2: Разделите раунды
| количество раундов | иллюстрировать | выход | производить | остальной |
|---|---|---|---|---|
| первый раунд | Выполнить вывод внешней синхронизации | 1 | Задача макроса: нет Микрозадачи: then1
|
Задача макроса: нет Микрозадачи: then1
|
| второй раунд | Выполнение микрозадачthen1
|
2, 3 | Задача макроса: нет Микрозадачи: then2、then4
|
Задача макроса: нет Микрозадачи: then2、then4
|
| третий раунд | Выполнение микрозадачthen2,then4
|
4, 6 | Задача макроса: нет Микрозадачи: then3
|
Задача макроса: нет Микрозадачи: then3
|
| четвертый раунд | Выполнение микрозадачthen3
|
5 | Задача макроса: нет Микрозадачи: нет |
Задача макроса: нет Микрозадачи: нет |
Четвертый проход
Этот на один больше предыдущегоreturn
Я сказал раньше,thenметод автоматически вернет новыйPromise, эквивалентноreturn new Promise, а если вручную написатьreturn Promise,Этоreturnэто вы вручную написали этоPromise
new Promise((resolve, reject) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
// 多了个return
return new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
}).then(() => { // 相当于return了这个then的执行返回Promise
console.log(5)
})
}).then(() => {
console.log(6)
})
Шаг 1: Отметить + Преобразовать
из-заreturnдаthen3выполнение возвращаетPromise,такthen4Фактическиthen3Promise.then(), поэтому его можно преобразовать вthen3.then4
new Promise((resolve, reject) => {
console.log(1) // 同步
resolve()
}).then(() => { // 异步:微任务 then1
console.log(2) // then1 中的 同步
new Promise((resolve, reject) => {
console.log(3) // then1 中的 同步
resolve()
}).then(() => { // 异步:微任务 then2
console.log(4)
}).then(() => { // 异步:微任务 then3
console.log(5)
}).then(() => { // 异步:微任务 then4
console.log(6)
})
})
Шаг 2: Разделите раунды
| количество раундов | иллюстрировать | выход | производить | остальной |
|---|---|---|---|---|
| первый раунд | Выполнить вывод внешней синхронизации | 1 | Задача макроса: нет Микрозадачи: then1
|
Задача макроса: нет Микрозадачи: then1
|
| второй раунд | Выполнение микрозадачthen1
|
2, 3 | Задача макроса: нет Микрозадачи: then2、then3、then4
|
Задача макроса: нет Микрозадачи: then2、then3、then4
|
| третий раунд | Выполнение микрозадачthen2、then3、then4
|
4, 5, 6 | Задача макроса: нет Микрозадачи: нет |
Задача макроса: нет Микрозадачи: нет |
Уровень 5
new Promise((resolve, reject) => {
console.log(1)
resolve()
}).then(() => {
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
}).then(() => {
console.log(5)
})
}).then(() => {
console.log(6)
})
new Promise((resolve, reject) => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})
Шаг 1: Отметить + Преобразовать
const p1 = new Promise((resolve, reject) => { // p1 是 then1 执行返回的新 Promise
console.log(1) // 同步
resolve()
}).then(() => { // 异步:微任务 then1
console.log(2)
const p2 = new Promise((resolve, reject) => { // p2 是 then2 执行返回的新 Promise
console.log(3) // then1 里的 同步
resolve()
}).then(() => { // 异步:微任务 then2
console.log(4)
// 拿着 p2 重新 then
p2.then(() => { // 异步:微任务 then3
console.log(5)
})
})
// 拿着 p1 重新 then
p1.then(() => { // 异步:微任务 then4
console.log(6)
})
})
new Promise((resolve, reject) => {
console.log(7) // 同步
resolve()
}).then(() => { // 异步:微任务 then5
console.log(8)
})
Шаг 2: Разделите раунды
| количество раундов | иллюстрировать | выход | производить | остальной |
|---|---|---|---|---|
| первый раунд | Выполнить вывод внешней синхронизации | 1, 7 | Задача макроса: нет Микрозадачи: then1、then5
|
Задача макроса: нет Микрозадачи: then1、then5
|
| второй раунд | Выполнение микрозадачthen1、then5
|
2, 3, 8 | Задача макроса: нет Микрозадачи: then2、then4
|
Задача макроса: нет Микрозадачи: then2、then4
|
| третий раунд | Выполнение микрозадачthen2、then4
|
4, 6 | Задача макроса: нет Микрозадачи: then3
|
Задача макроса: нет Микрозадачи: then3
|
| четвертый раунд | Выполнение микрозадачthen3
|
5 | Задача макроса: нет Микрозадачи: нет |
Задача макроса: нет Микрозадачи: нет |
Уровень 6
фактическиasync/awaitПринцип внутренней реализации зависит отPromise.prototype.thenПостоянная вложенность, ее также можно преобразовать в заголовок, о чем будет сказано ниже.
Заинтересованные друзья могут прочитать мою статью7 картинок, принцип async/await, которые можно сделать за 20 минут! Зачем так долго[Объем чтения: 1,8 Вт, лайков: 571]
async function async1() {
console.log(1);
await async2();
console.log(2);
}
async function async2() {
console.log(3);
}
console.log(4);
setTimeout(function () {
console.log(5);
});
async1()
new Promise(function (resolve, reject) {
console.log(6);
resolve();
}).then(function () {
console.log(7);
});
console.log(8);
Шаг 1: Отметить + Преобразовать
Примечание. Преобразование здесь только для вопроса, его легче понять, обычно не делайте этого преобразования, обычно это преобразование не подходит.
console.log(4); // 同步
setTimeout(function () {
console.log(5); // 异步:宏任务 setTimeout
});
// async1函数可转换成
console.log(1) // 同步
new Promise((resolve, reject) => {
console.log(3) // 同步
resolve()
}).then(() => { // 异步:微任务 then1
console.log(2)
})
// async1函数结束
new Promise(function (resolve, reject) {
console.log(6); // 同步
resolve();
}).then(function () { // 异步:微任务 then2
console.log(7);
});
console.log(8); // 同步
Шаг 2: Разделите раунды
| количество раундов | иллюстрировать | выход | производить | остальной |
|---|---|---|---|---|
| первый раунд | Выполнить синхронный вывод | 4, 1, 3, 6, 8 | Задача макроса:setTimeoutМикрозадачи: then1、then2
|
Задача макроса:setTimeoutМикрозадачи: then1、then2
|
| второй раунд | Выполнение микрозадачthen1、then2
|
2, 7 | Задача макроса: нет Микрозадачи: нет |
Задача макроса:setTimeoutМикрозадачи: нет |
| третий раунд | Выполнять задачи макросаsetTimeout
|
5 | Задача макроса: нет Микрозадачи: then5
|
Задача макроса: нет Микрозадачи: нет |
домашнее задание после уроков
Напоследок дам вам две операции, которые помогут закрепить полученные знания из статьи, вы можете присоединиться к моей группе рыбалки в мутной воде, быть答案обсуждение. Чтобы присоединиться к группе нажмите здесьв группу, было почти1000人Присоединяйтесь к исследованию, буду проводить его регулярно学习分享,模拟面试Ждите обучающих мероприятий, учитесь вместе и прогрессируйте вместе! ! !
Вопрос 1 (вопрос для размышления)
Подумайте о следующих двух, в чем разница?
// 第一种
const p = new Promise((resolve, reject) => {
resolve()
}).then(() => console.log(1)).then(() => console.log(2))
// 第二种
const p = new Promise((resolve, reject) => {
resolve()
})
p.then(() => console.log(1))
p.then(() => console.log(2))
Вопрос 2 (без проблем)
async function async1() {
console.log(1);
await async2();
console.log(2);
}
async function async2() {
console.log(3);
}
new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
console.log(4)
}, 1000);
}).then(() => {
console.log(5)
new Promise((resolve, reject) => {
setTimeout(() => {
async1()
resolve()
console.log(6)
}, 1000)
}).then(() => {
console.log(7)
}).then(() => {
console.log(8)
})
}).then(() => {
console.log(9)
})
new Promise((resolve, reject) => {
console.log(10)
setTimeout(() => {
resolve()
console.log(11)
}, 3000);
}).then(() => {
console.log(12)
})
Вопрос 3 (несколько сложный)
Этот вопрос может一分钟内Найдите меня, чтобы получить награду. Этот вопрос должен иметь определенные требования.Promise原理基础 + async/await原理基础Легче ответить правильно.Заинтересованные студенты могут прочитать статьи, которые я написал ранее.
- После прочтения принцип написания промиса от руки, самая понятная версия[Чтение: 1,1 Вт, лайков: 430]
- 7 картинок, принцип async/await, которые можно сделать за 20 минут! Зачем так долго[Объем чтения: 1,8 Вт, лайков: 571]
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async start')
return new Promise((resolve, reject) => {
resolve()
console.log('async2 promise')
})
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0);
async1()
new Promise((resolve) => {
console.log('promise1')
resolve()
}).then(() => {
console.log('promise2')
}).then(() => {
console.log('promise3')
})
console.log('script end')
Эпилог
Если вы считаете, что эта статья вам немного поможет, поставьте лайк и поддержите Линь Сансиня, ха-ха. Или присоединяйтесь к моей группе, ха-ха, давайте ловить рыбу и учиться вместе: meron857287645