Понимание SetTimeout, SetInterVal, setImmediate и process.nextTick

внешний интерфейс

отплыть

Раньше я читал статьи других людей о Наггетс, так что, пожалуйста, поставьте мне лайк. Когда я посещаю github, мне просто нравится смотреть исходный код других людей, и я никогда ничего не загружаю на git. Сегодня мне вдруг захотелось кое-что опубликовать, чтобы я мог проверить это позже, и я также могу дать вам несколько советов, чтобы убедиться, что это правильно. Чтоб не идти все дальше и дальше по ложному пути, и спорить с нужным краснолицым человеком. Ведь раз вы установили неправильную точку зрения, то неправильная точка зрения станет правильной в вашем подсознании.

сомневаться

При нормальных обстоятельствах setTimeout, setInterval, setImmediate и process.nextTick выполняются асинхронно, так каков же механизм и время выполнения этих четырех методов функций и в чем между ними разница? Можно ли его заменить? Все это должно начинаться с цикла событий nodejs. Понимать это.

setTimeout и setInterval

Сначала представим каждую функцию отдельно, setTimeout и setInterval наиболее похожи.Из анализа функций мы знаем, что форматы функций setTimeout и setInterval следующие:

    setTimeout(function(arg1,arg2){
        //some code
    },XXX)
    setInterval(function(arg1,arg2){
        //some code
    },XXX)

Тогда это время задержки XXX имеет условие, и диапазон времени задержки составляет [1,2^31-1]. Когда вы устанавливаете время задержки меньше 1 или больше 2^31-1, время задержки изменяется на 1 по умолчанию, то есть когда вы пишете setTimeout(function(arg1,arg2){},0.1), на самом деле это эквивалентно написанию setTimeout (function(arg1,arg2){},1).

setImmediate и nextTick

Посмотрим непосредственно на код, каков результат выполнения этих двух функций:

   setImmediate(function(){
    console.log('immediate')
   })
   process.nextTick(function(){
    console.log('next tick')
   })

Порядок замены кода

   process.nextTick(function(){
    console.log('next tick')
   })
   setImmediate(function(){
    console.log('immediate')
   })

Мы обнаружили, что вывод кода такой же. Тогда механизм выполнения nextTick предшествует setImmediate.

Механизм исполнения 4 функций

Прежде чем представить механизм 4-х функций, давайте рассмотрим интересное явление.

setTimeout(() => {
    console.log('setTimeout')
}, 0)

setImmediate(() => {
    console.log('setImmediate')
})

вместо вывода результата

setTimeout
setImmediate

иногда

setImmediate
setTimeout

Почему порядок выполнения этих двух функций так несовместим? Есть ли случайность? На самом деле нет, давайте посмотрим на картинку:

Это схематическая диаграмма всего цикла событий Я удалил много вещей и сократил подробные операции ввода-вывода за один шаг. Воспользуемся популярным методом расстояния, уровни setTimeout и setInterval одинаковые, поэтому методы прописаны и выполняются последовательно в коде. Но, согласно приведенному выше выводу кода, почему на шагах 1 и 3 происходит случайный вывод?

Функция обратного вызова setTimeout выполняется на этапе 1, а функция обратного вызова setImmediate выполняется на этапе 3. Цикл событий сначала определяет этап 1, что правильно. В официальном документе также говорится, что Цикл цикла событий - это таймеры -> ввод-вывод -> немедленные, промывка и повторение.Но есть проблема, что время входа в первое событие петля неопределенная, не обязательно с нуля

Время, введенное в приведенном выше примере, не является полным. Кто-то в Интернете сделал вывод, что при входе в цикл событий Если время меньше 1 мс, он войдет в этап проверки, то есть этап 3, и вызовет setImmediate.Если он превышает 1 мс, он войдет в этап таймера, который является этапом 1, и вызовет функцию обратного вызова setTimeout. .

Таким образом, мы можем обобщить механизм 4 функций: на этапе 1 (этап таймера) мы регистрируем функции обратного вызова setTimeout и setInterval, а на этапе 3 (этап проверки) после этапа ввода-вывода мы регистрируем обратный вызов функции setImmediate. . Теперь осталась только функция process.nextTick. Эта функция довольно специфична, время ее регистрации находится в фазе галочки зеленой стрелки на рисунке выше.

Используйте несколько вопросов, чтобы углубить свое понимание

Тема 1

const fs = require('fs')

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('setTimeout')
  }, 0)

  setImmediate(() => {
    console.log('setImmediate')
  })
})

результат операции:

setImmediate 
setTimeout

причина:

таймер -- ввод/вывод -- проверка. Эти три этапа представляют собой последовательность выполнения цикла событий. Когда fs читает файл, мы регистрируем setTimeout и setImmediate в цикле событий. Когда поток файла fs читается, выполняется этап ввода-вывода, а затем выполняется проверка Этап, выполните функцию обратного вызова setImmediate, а затем перейдите к этапу таймера, чтобы выполнить setTimeout при следующем опросе.

тема вторая

setInterval(() => {
  console.log('setInterval')
}, 100)

process.nextTick(function tick () {
  process.nextTick(tick)
})

результат операции:

无任何输出,setInterval永远不执行

причина:

Поскольку process.nextTick регистрируется в фазе тика, обратный вызов по-прежнему является методом process.nextTick, но process.nextTick не регистрируется в фазе тика следующего опроса, а склеивается в фазе текущего тика и продолжает выполняться. выполнить, что приводит к бесконечному циклу, цикл событий вообще не имеет шансов войти в стадию таймера

### Тема третья

setImmediate(() => {  ------ 1
  console.log('setImmediate1')
  setImmediate(() => {  ------2
    console.log('setImmediate2')
  })
  process.nextTick(() => { -------3
    console.log('nextTick')
  })
})

setImmediate(() => { ------4
  console.log('setImmediate3')
})

результат операции:

setImmediate1
setImmediate3
nextTick
setImmediate2

причина:

Сначала зарегистрируйте первый setImmediate на самом внешнем уровне, то есть с меткой 1, а затем зарегистрируйте setImmediate на самом внешнем уровне с меткой 2. Затем зарегистрируйте асинхронную функцию в первом setImmediate. Сначала зарегистрируйте функцию setImmediate с номером 3, а затем зарегистрируйте process.nextTick с номером 4. В это время войдите в цикл событий, чтобы выполнить обратный вызов, сначала выполните функцию в 1 и выведите setImmediate 1. Поскольку и 3, и 4 зарегистрированы после 2, в это время выполняется метод обратного вызова, помеченный 4, и выводится setImmediate3. Продолжайте опрашивать, т.к. process.nextTick прописан в тике после 4, поэтому сначала выполните process.nextTick, лучше всего опрашивать callback-метод исполнения 2, и выводить setImmediate2

тема четвертая

const promise = Promise.resolve()

promise.then(() => {
  console.log('promise')
})

process.nextTick(() => {
  console.log('nextTick')
})

Выходной результат:

nextTick
promise

причина:

promise.then также регистрируется в фазе тика, но process.nextTick имеет более высокий приоритет, чем promise, поэтому сначала вызовите process.nextTick

пятая тема

setTimeout(() => {
  console.log(1)
}, 0)
new Promise((resolve, reject) => {
  console.log(2)
  for (let i = 0; i < 10000; i++) {
    i === 9999 && resolve()
  }
  console.log(3)
}).then(() => {
  console.log(4)
})
console.log(5)

Выходной результат:

2
3
5
4
1

причина:

новое обещание является синхронной операцией, поэтому она выводит 2 и 3, а затем выполняет последнюю строку кода для вывода 5. Далее проблема с promise.then и setTimeout. Мы знаем, что promise.then, как и process.nextTick, регистрируется в фазе тика, а setTimeout — в фазе таймера, сначала он входит в фазу тика для выполнения, а затем входит в setTimeout следующего опроса.

тема шесть

setImmediate(() => {
    console.log(1)
    setTimeout(() => {
        console.log(2)
    }, 100)
    setImmediate(() => {
      console.log(3)
    })
    process.nextTick(() => {
      console.log(4)
    })
  })
  setImmediate(() => {
    console.log(5)
    setTimeout(() => {
      console.log(6)
    }, 100)
    setImmediate(() => {
      console.log(7)
    })
    process.nextTick(() => {
      console.log(8)
    })
  })

выходной результат

1
5
4
8
3
7
2
6

причина:

Галочки здесь объединены, поэтому 4 и 8 выводятся последовательно

тема семь

setImmediate(() => { ---1
  console.log(1)
  setTimeout(() => {   ---2
    console.log(2)
  }, 100)
  setImmediate(() => { ---3
    console.log(3)
  })
  process.nextTick(() => { ---4
    console.log(4)
  })
})
process.nextTick(() => { ---5
  console.log(5)
  setTimeout(() => { ---6
    console.log(6)
  }, 100)
  setImmediate(() => { ---7
    console.log(7)
  })
  process.nextTick(() => { ---8
    console.log(8)
  })
})
console.log(9)

выходной результат

9
5
8
1
7
4
3
6
2

причина: как показано на рисунке

Пополнить: 1.макротаск: код в скрипте, setTimeout, setInterval, ввод-вывод, визуализация пользовательского интерфейса.

2.микрозадача: обещание, Object.observe, MutationObserver, process.nextTick.