Event Loop оказался делом

JavaScript
Event Loop оказался делом

передовой

Жила-была гора, а в горе был храм, В храме был маленький монах, рассказывавший истории, о чем он говорил? Говоря о:

Жила-была гора, а в горе был храм, В храме был маленький монах, рассказывавший истории, о чем он говорил? Говоря о:

Жила-была гора, а в горе был храм, В храме был маленький монах, рассказывавший истории, о чем он говорил? Говоря о:

...

Пожалуйста, прочитайте короткий рассказ

image

Раньше был ресторан с владельцем и шеф-поваром, он начал свой бизнес, сначала у него не было средств на найм сотрудников, поэтому он стал владельцем и официантом.

Есть четыре типа клиентов, которые приходят в этот ресторан

  1. Супер VIP-клиент
  2. VIP-клиенты
  3. обычный клиент
  4. просроченные клиенты

Как VIP-клиент, вы должны иметь VIP-привилегии.

  • Приоритетное обслуживание
  • Тот же VIP, первый заказ обслужен первым

Поэтому порядок обслуживания в этом магазине связан с идентичностью и порядком заказа.

Супер VIP-клиент > VIP-клиенты > обычный клиент > просроченные клиенты

Однажды, после того как босс открылся, несколько человек зашли один за другим, чтобы заказать и поесть.

  • Первый, кто войдет, этообычныйклиентов, заказавшихвареное мясо

  • Второй пришел с деньгами.VIPзаказ покупателярак

  • Вошел третийПо умолчаниюклиентов, которые заказали много

  • Приходит четвертый -Супер VIP-клиент, нажалМаринованная рыба

Поскольку в этом ресторане всего один человек, босс развлек их и пошел жарить после того, как они заказали. Согласно указанному выше порядку, так будет жарить первымСупер VIP-клиентзаказ, а затемVIP-клиентызаказ, а затемобычный клиентЗаказали блюда, наконец-то приехалипросроченные клиентызаказ

давайте использоватьПоддельный кодПосмотрите, как реализовать эту логику

image

Мы определяем четыреfunction

  • superVipOrder(имя, блюдо) используется для представленияСупер VIP-пользовательЗаказ а ля карт
  • vipOrder(имя, блюдо) используется для представленияVIP-пользователиЗаказ а ля карт
  • заказ (имя, блюдо) используется для представленияобычный пользовательЗаказ а ля карт
  • groupOrder(имя, блюдо) используется для представленияпросроченные клиентыЗаказ а ля карт

В соответствии с правилами обслуживания, упомянутыми выше,

Супер VIP-клиент > VIP-клиенты > обычный клиент > просроченные клиенты

Фактический порядок обслуживания, который мы можем знать,

Так вот вопрос, теfunctionкто они такие?

На самом деле все очень просто, этиfunctionВсе соответствуют некоторым асинхронным функциям в JavaScript.

// 超级VIP客户
// 微任务,将回调加入到  执行队列,优先执行
function superVipOrder(name, dish, order) {
  process.nextTick(() => {
    console.log(red(`superVip顾客 ${name} 点了 ${dish} 已经上了`));
  });
}
// VIP客户
// 微任务,将回调加入到  执行队列,优先执行,优先级比process.nextTick低
function vipOrder(name, dish, order) {
  new Promise((resolve, reject) => {
    resolve(true);
  }).then(() => {
    console.log(blue(`vip顾客 ${name} 点了 ${dish} 已经上了`));
  });
}
// 普通客户下单
// 宏任务,将回调加入到 宏任务的执行队列中
function order(name, dish, order) {
  setTimeout(() => {
    console.log(yellow(`普通顾客 ${name} 点了 ${dish} 已经上了`));
  }, 0);
}
// 赖账的客户下单
function groupOrder(name, dish, order) {
  setImmediate(() => {
    console.log(green(`赖账的顾客 ${name} 点了 ${dish} 已经上了`));
  });
}

Мы можем поставитьprocess.nextTickдумаю, что этосупер вип пользователь, наивысший приоритет,

РоднойPromiseдумаю, что этоVIP-пользователь, приоритет выполнения высокий

setTimeoutдумаю, что этообычный пользователь, приоритет выполнения нормальный

setImmediateдумаю, что этопросроченный клиент, приоритет выполнения низкий

восстановить псевдокод

Мы восстановим псевдокод этих асинхронных функций, что сделает нас более интуитивными и добрыми.

image

В соответствии с правилами приоритета, упомянутыми в приведенной выше истории, мы знаем, что вывод будет таким:

Почему это результат? Поговорим о цикле событий в JavaScript.

Event Loop

1. Цикл событий JavaScript

image

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

Итак, в js задачи делятся на синхронные задачи и асинхронные задачи,

  • Задача синхронизации -> официант идет встречать гостей, чтобы заказать еду
  • Асинхронная задача -> готовка шеф-поваром, асинхронная функция обратного вызова эквивалентна подаче еды официантом

image

Цикл событий JS показан на рисунке,

  1. При выполнении задачи основного потока, если есть асинхронная задача, она войдет в Таблицу событий и зарегистрирует функцию обратного вызова.Когда указанное дело будет выполнено, функция обратного вызова будет помещена вcallback queueсередина
  2. После того, как основной поток завершит выполнение, он будет читатьcallback queueФункция обратного вызова в , входит в основной поток для выполнения
  3. Этот процесс повторяется непрерывно, что часто называют Event Loop (цикл событий).

2. Асинхронные задачи

Асинхронные задачи делятся на макрозадачи и микрозадачи, разница между которыми в основном заключается в разнице в порядке выполнения.

В js микрозадачи есть

  • Родной Promise -> на самом деле то, о чем мы упоминали выше.VIP-пользователи,

  • process.nextTick -> на самом деле то, что мы упоминали вышеСупер VIP-пользователь,

process.nextTickприоритет выполнения выше, чемPromiseиз

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

  • общий код скрипта
  • setTimeout -> на самом деле то, что мы упоминали вышеобычный пользователь,
  • setImmediate -> на самом деле то, что мы упоминали вышегруппа пользователей,

setTimeoutприоритет выполнения выше, чемsetImmediateиз

Процесс выполнения макрозадач и микрозадач

В цикле событий JS сначала выполнит весь кодscript, после выполнения он определит, есть ли микрозадачи в очереди микрозадач, и если да, то выполнит их одну за другой и одновременно выполнит макрозадачи. такой процесс

image

тестовое задание

Посмотрим, понимает ли кусок кода этот процесс

<script>
    setTimeout(() => {
      console.log('a');
      new Promise( res => {
        res()
      }).then( () => {
        console.log('c');
      })
      process.nextTick(() => {
        console.log('h');
      })
    }, 0)
    console.log('b');
    
    process.nextTick( () => {
      console.log('d');
      process.nextTick(() => {
        console.log('e');
        process.nextTick(() => {
          console.log('f');
        })
      })
    })
    
    setImmediate( () => {
      console.log('g');
    })
</script>

Результат выполнения:b d e f a h c g

Давайте проанализируем поток выполнения этого кода

  1. Выполнение первой задачи макроса на главной странице Весь абзацscriptКод этикетки, встретите первыйsetTimeout, добавьте его функцию обратного вызова вмакросочередь задач,

  2. выходconsole.log('b')

  3. При встрече с process.nextTick добавьте его функцию обратного вызова вмикроЗадача

  4. При встрече с setImmediate добавьте его функцию обратного вызова вмакросочередь задач

Задача макроса Очередь событий Очередь событий микрозадач
setTimeout process.nextTick
setImmediate
  1. Когда выполняется первая задача макроса, она определяет, есть ли ещемикрозадача только однамикроЗадача, выполнить обратный вызов process.nextTick, выводconsole.log('d'), а затем столкнулся с process.nextTick, который был помещен вмикроочередь задач
  2. продолжатьмикроФункция обратного вызова из очереди задач вынимается, продолжает выполняться и выводитсяconsole.log('e'), а затем столкнулся с process.nextTick, который был помещен вмикроочередь задач
  3. продолжатьмикроФункция обратного вызова из очереди задач вынимается, продолжает выполняться и выводитсяconsole.log('f'), а затем столкнулся с process.nextTick, который был помещен вмикроочередь задач
Задача макроса Очередь событий Очередь событий микрозадач
setTimeout
setImmediate
  1. Когда очередь микрозадач пуста, запустите новую макрозадачу, выньте функцию первой очереди макрозадач,setTimeout,воплощать в жизньconsole.log('a'), а потом столкнутьсяPromise,process.nextTickдобавить свой обратный вызов вмикроочередь задач. После казни
Задача макроса Очередь событий Очередь событий микрозадач
setImmediate promise.then
- process.nextTick
  1. продолжать судитьмикроЕсть ли в очереди задач функция обратного вызова для выполнения, потому чтоprocess.nextTickимеет приоритет выполнения больше, чемpromise, поэтому он будет выполняться первымprocess.nextTickобратный вызов, выводconsole.log('h');, если их несколькоprocess.nextTickобратный звонок, будетprocess.nextTickВсе обратные вызовы будут выполняться после выполнения другихмикроОбратный вызов для задачи. Когда все обратные вызовы nextTick будут выполнены, выполнитеpromiseобратный вызов, выводconsole.log('c');, пока очередь обратного вызова обещания не будет выполнена, он будет судить, есть ли ещемикроЗадача.
Задача макроса Очередь событий Очередь событий микрозадач
setImmediate
  1. микрозадачиПосле выполнения начать выполнять новую задачу макроса, выполнитьsetImmediateобратный вызов, выводconsole.log('g');

3. setImmediate

почему здесьsetImmediateВынеси его отдельно, потому что он относится к разряду макро задач, но он немного другой.

Посмотрите на кусок кода

image

Согласно нашей логике анализа выше, мы будем думать, что вывод этого кода должен бытьa b c d. Если мы используем Node 0.10.x для выполнения этого кода, результатом будет действительно выводa b c d

Однако после версий Node выше 4.x после выполненияsetImmediateДа, цикл while будет использоваться для удаления всех немедленных обратных вызовов и их обработки по очереди.

ps: в узле 11 и выше setImmediate обрабатывается в соответствии с порядком выполнения макрозадач и не будет вызываться в цикле.

image

Наконец, взгляните на фрагмент кода, чтобы убедиться, что вы действительно его освоили.

Если вы еще не освоили его, добро пожаловать в комментарии

image

<script>
   console.log("start");
   process.nextTick(() => {
     console.log("a");
     setImmediate(() => {
       console.log("d");
     });
     new Promise(res => res()).then(() => {
       console.log("e");
       process.nextTick(() => {
         console.log("f");
       });
       new Promise(r => {
         r()
       })
       .then(() => {
         console.log("g");
       });
       setTimeout(() => {
         console.log("h");
       });
     });
   });
   
   setImmediate(() => {
     console.log("b");
     process.nextTick(() => {
       console.log("c");
     });
     new Promise(res => res()).then(() => {
       console.log("i");
     });
   });
   console.log("end");
</script>

Результат:start end a e g f h b d c i

Кратко проанализируем код:

  1. Начинается первый раунд цикла событий, выполняяscriptкод,выход start end,Будуprocess.nextTickОбратный вызов добавляется в очередь микрозадач, аsetImmediateОбратный вызов добавляется в очередь задачи макроса
  2. Выполнение очереди микрозадачprocess.nextTickобратный звонок,выход a,БудуsetImmediateОбратный вызов добавлен вмакросВ очереди заданий встретитеpromise, добавьте обратный вызов вмикров очереди задач.
задача макроса микрозадачи
setImmediate promise.then
setImmediate -
  1. Продолжайте выполнять обратный вызов в очереди микрозадачи, выньтеpromise.thenи выполнить,выходe,Будуprocess.nextTickОбратный вызов помещается в микрозадачу и встречаетpromise, добавьте обратный вызов вмикров очереди задач.
  2. Определить, есть ли еще функция обратного вызова в очереди обратного вызова текущего промиса, которая не была выполнена.Если это так, она продолжит выполнение и уберет обратный вызов только что введенного промиса.выход g, когда очередь обратного вызова Promise выполняется, продолжайте судить, есть ли еще микрозадачи.
  3. выигратьprocess.nextTickобратный вызов и выполнение,выходf
задача макроса микрозадачи
setImmediate -
setImmediate -
setTimeout -
  1. После того, как текущая очередь микрозадач опустеет, начните выполнение задачи макроса, т.к.setTimeoutприоритет больше, чемsetImmediate, так выньsetTimeoutобратный вызов и выполнение,выходh
  2. Текущая очередь микрозадач все еще пуста, начните выполнение макрозадачи и выньте всеsetImmediateфункцию обратного вызова и выполнить,выходb d,Будуprocess.nextа такжеpromiseОбратный вызов помещается в очередь микрозадачи.
  3. выигратьмикрозадачиФункция обратного вызова в очереди и выполняется,выход c i

Суммировать

Как часто задаваемый вопрос в интервью, Event Loop несложно понять.

Оригинальная ссылка:C code.live/Len too/list…

Добро пожаловать, чтобы следовать

Добро пожаловать в общедоступный номер»разработка кода», делясь последней технической информацией каждый день

image