Порядок вывода SETTIMEOUT + PROMISE + ASYNC? Очень простой!

внешний интерфейс JavaScript
Порядок вывода SETTIMEOUT + PROMISE + ASYNC? Очень простой!

Эта статья участвовала в "Проект «Звезда раскопок»”, чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.

предисловие

Всем привет, меня зовут Линь Сансинь, оEventLoopОчков знаний в тесте в обычное время много, но они также тесно связаны с нашей повседневной работой.EventLoopПорядок выполнения может очень помочь нам найти проблему. На самом деле нормальноEventLoopПорядок легко сказать, но еслиsetTimeout + Promise + async/awaitОбъединить усилия очень сложно. Сегодня я возьму тебя过五关斩六将, победить их! ! !

Примечание. В этой статье не рассматривается механизм выполнения Nodejs.

Синхронный Асинхронный

Что такое асинхронный и что синхронный, много говорить не буду, расскажу через небольшую историю.

  • 同步: Вы звоните в книжный магазин, чтобы заказать книги, начальник говорит я проверю, вы не вешаете трубку и ждете, начальник скажет вам результаты, вы не можете заниматься своими делами в этот период
  • 异步: Ты звонишь в книжный магазин, чтобы заказать книги, начальник сказал, я посмотрю, я тебе потом скажу, ты кладешь трубку и иди сначала занимайся своими делами.

Механизм выполнения JS

На самом деле это не сложно, механизм выполнения кода JavaScript, я сведу его к трем предложениям

  • 1. встреча同步代码прямое исполнение
  • 2. встреча异步代码отложи это и положи его回调函数сохранить его, сохранить его事件队列
  • 3. Ждать всех同步代码все выполняются, а затем из事件队列все хранится в异步回调函数Выньте его и выполните по порядку

截屏2021-10-04 下午8.11.18.png

См. пример ниже

console.log(1) // 同步
setTimeout(() => {
  console.log(2) // 异步
}, 2000);
console.log(3) // 同步
setTimeout(() => {
  console.log(4) // 异步
}, 0);
console.log(5) // 同步

输出 : 1 3 5 4 2

截屏2021-10-04 下午9.11.39.png

Макрозадачи и микрозадачи

Как упоминалось ранее, после выполнения всего кода синхронизации начните с事件队列выполнять все подряд异步回调函数.

фактически事件队列Это тоже небольшая группа, а у других свои правила, это похоже на школу, которая управляет многими клубами, и у их собственных клубов тоже свои правила.

Опять же, почему事件队列Вам нужны свои правила? В противном случае, вы должны сначала подумать, почему клубы в школе имеют свои правила и оценки.Это потому, что у одних людей сильные способности, а у других слабые, поэтому есть уровни уровней. фактически事件队列тоже самое,事件队列Он используется для хранения асинхронных обратных вызовов, но асинхронные также делятся на типы.Асинхронные задачи делятся на宏任务а также微任务,а такжеМикрозадачи выполняются раньше макрозадач

Что такое макрозадачи и микрозадачи?

задача макроса

# браузер Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

микрозадачи

# браузер Node
Promise.prototype.then catch finally
process.nextTick
MutationObserver

Процесс реализации

Итак, давайте поговорим об общем процессе выполнения.

截屏2021-10-05 下午4.37.02.png

пример

Вы можете следовать моим шагам по решению проблем, в основном 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原理基础Легче ответить правильно.Заинтересованные студенты могут прочитать статьи, которые я написал ранее.

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