Pull to refresh

Comments 26

Полезный текст для прохождения собеседований.

Но, честно говоря, не хочется тащить в прод такой код, где прям нужны такие знания или идет опора в определении последовательность операций именно на то как работает event loop

Скорее всего он уже в проде внутри реакта/ангуляра/вью

Эти знания нужны чаще в момент отладки, чтоб можно было объяснить себе и всем заинтересованным почему оно работает именно так. Часто бывает что мы что-то грузим в 3-5 местах и вдруг что-то загружается раньше времени и вот тут как раз понимание ивент лупа очень кстати.

1746 это некий случайный идентификатор. Он к нашему ответу отношения не имеет.
Если кто может более точно объяснить почему он тут появляется, прошу в комментарии.

Когда что-то выполняешь вручную в консоли, оно пишет результат последней синхронной операции. Здесь последним был setTimeout, который вернул id созданного таймаута (тот самый, на который можно натравить clearTimeout). Да, это просто число.

В продакшене я бы не стал на такое полагаться. Даже на то что два сетТаймаута с одинаковой задержкой выполнятся в порядке их создания — не надо полагаться.

На собеседовании хорошо при таких вопросах отстоять позицию того что есть сейчас, а есть «потом» и это «потом» будет синхронизировано через более надёжные механизмы. Если работодатель настаивает, то можно выполнить код в консоли. Если хочет решения на бумажке, то встать и уйти, нервы дороже.

Интересно освежить в памяти.

В задаче 3 название колонок 2 и 3 перепутаны.

1746 - айди от setTimeout, который можно использовать в clearTimeout, дабы удалить таймер

вообще интересная статья
если интересно, то можешь еще добавить requestAnimationFrame
и + есть подвохи в триггерных кликах https://jsfiddle.net/e5fqrapb/

В 4 задаче не сходится. Все setTimeout в функции myPromise будут идти и регистрироваться синхронно, в потоке основного кода. Соответственно всё что вы занесли в микрозадачи должны по идее быть в макрозадачах. Затем, когда финально собираем ответ:

  1. Срабатывает in 'setTimeout2' через 100 мс

  2. 'in setTimeout1' через 1000

  3. Через 1000 мс резолвится первый промис (запускается колбек, который добавляет в очередь микротасок колбек с console.log('in Promise 1'))

  4. Далее цикл событий видит, что в очереди микротасок появилось задание, он тут же его выполняет (console.log('in Promise 1'))

  5. Очередь микротасок пуста, следовательно возвращаемся к макротаскам => Через 1000 мс резолвится второй промис (запускается колбек, который добавляет в очередь микротасок колбек с console.log('in Promise 3'))

  6. Далее цикл событий видит, что в очереди микротасок появилось задание, он тут же его выполняет (console.log('in Promise 3'))


и т.д.

не совсем понял, что ты имеешь ввиду под словом "не сходится". Порядок выполнения вроде верный.

сейчас попробую объяснить. Просто такое ощущение, что ты не до конца разобрался с этой задачей и в итоге не совсем правильно объяснил последовательность и механику, каким образом этот код будет выполняться (проще говоря "подогнал" ответ под решение). Соответственно, новички, читающие статью, могут либо запутаться в этом примере либо вообще не понять в итоге, как там всё происходит.

Смотри,

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

тебя не смутило, что после этой фразы микрозадачи не добавляются по одной в конец очереди макрозадач (как это должно быть и как это было в задаче 2 с console.log(4), ты там сам писал, что она идет в конец очереди макрозадач и это правильно, тут не поспоришь), вместо этого они как бы перемешиваются с уже существующими в очереди макрозадачами, передвигаясь просто вправо.

Ответ прост: ИЗНАЧАЛЬНО все задачи в очереди микрозадач не должны быть в колонке с микрозадачами. Они должны идти в колонке с макрозадачами, потому что как я уже писал:

Все setTimeout в функции myPromise будут идти и регистрироваться синхронно, в потоке основного кода

И только потом, при разборе очереди макрозадач по одной на небольшой промежуток времени эти задачи попадают в список микрозадач (так как резолвится этот промис) и тут же выполняются (так как после каждой макротаски идет выполнение микротасок - при наличии их в очереди). Как раз это я показал в первом комментарии, в своей последовательности (список действий не полный, так как потом происходят аналогичные действия, смысла нет повторять)

Соглашусь, надо поправить, почему-то действительно биполярочка. Спасибо, что подметил!

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

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

нуу, да) так а что смущает? Если ты имел в виду первый проход по коду и в конец очереди макрозадач, то тут так всё и происходит. Они последовательно и идут в конец очереди макрозадач. Если говорить про проход по макрозадачам и переход в конец очереди микрозадач, то тут также всё логично и прозрачно. Они по одной (в последовательности исходя из задержки в setTimeout) переходят в конец очереди микрозадач (так как очередь пустая, то все они оказываются там всё время единственной задачей) и тут же выполняются.

Кстати, сейчас вроде в статье всё описал как надо)

вот смотри. все таки я понимаю, что есть у нас недопонимание.

у нас в основном потоке получается все это "промисы";
P1, 1000
P2, 2000
P3, 1000
P, 5000

а в списке макрозадач
ST1, 1000
ST2, 100
ST3, 2000
ST4,1000

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

ST1, 1000
ST2, 100
ST3, 2000
ST4,1000
P1, 1000
P2, 2000
P3, 1000
P, 5000

отсортируем по времени задержки

ST2,100
ST1,1000
ST4,1000 ___неверно! (он должен идти после P3)
P1,1000
P3,1000
ST3,2000
P2,2000
P,5000

а вот если мы их сразу будем регистрировать как макрозадачи, то тогда все ок

ST1,1000
P1,1000
ST2,100
P2,2000
ST3,2000
P3,1000
ST4, 1000
P,5000

сортируем:

ST2
ST1
P1
P3
ST4
P2
ST3
P


а в списке макрозадач
ST1, 1000
ST2, 100
ST3, 2000
ST4,1000

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

ST1, 1000
ST2, 100
ST3, 2000
ST4,1000
P1, 1000
P2, 2000
P3, 1000
P, 5000

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

  1. Добавили в список макрозадач ST1

  2. Добавили в список макрозадач P1

  3. Добавили в список макрозадач ST2

  4. Добавили в список макрозадач P2

  5. Добавили в список макрозадач ST3

  6. Добавили в список макрозадач P3

  7. Добавили в список макрозадач ST4

  8. Добавили в список макрозадач P

Затем идем по этому списку макрозадач в последовательности, которая регулируется параметром delay всех setTimeout в этой очереди

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

а вот если мы их сразу будем регистрировать как макрозадачи, то тогда все ок

ST1,1000
P1,1000
ST2,100
P2,2000
ST3,2000
P3,1000
ST4, 1000
P,5000

сортируем:

ST2
ST1
P1
P3
ST4
P2
ST3
P



Просто формулировка основной поток, она подразумевает сначала выполнение всех задач в основном потоке, потом уже микрозадачи, потом макрозадача, и повторяем. я понял, что вот этот момент что-то не улавливаю до конца. по факту, если у нас код типо такого,
console.log(1)
setTimeout(()=>console.log(2),0)
console.log(3)

То мы согласны, получим 1 3 2
Тк сначала выполнится основной поток, потом макрозадача

Добрый день. Опечатка небольшая в тексте под 3 задачей:

new Promise(resolve => setTimeout(resolve)).then(()=>console.log(4))

"Тут все ясно понятно. А вот что с четверкой? Микрозадача, порождает макрозадачу, обновим нашу табличку:"
Наверное имелось в виду - "Макрозадача (выполнение колэбэка setTimeout) порождает микрозадачу"

"Получается в момент выполнения микротасок, мы регистрируем макрозадачу, которая потом выполнит микрозадачу. Идем дальше."
Наверное имелось в виду "в момент выполнения executor'а регистрируется макротаска, при выполнении которой регистрируется микротаска"

По поводу ваших комментариев. Вы забываете что сначала setTimeout регистрируется в web api, а потом уже его коллбэк попадает в макротаску.

Вот упрощенная версия того, что происходит в этой задаче.

а что за сервис, позволяющий так посмотреть? или что-то самописное?

Касаемо третьей задачи, я согласен, выразился я не совсем ясно, и поэтому выглядит как опечатка или ошибка. Спасибо, исправлю.

"в момент выполнения executor'а регистрируется макротаска, при выполнении которой регистрируется микротаска"

именно это я и пытался сказать.

Есть два вида JS: для работы и для интервью.

Привет! На одном из собеседований была вот такая задача:

function log(x) {
return () => console.log(x);
}

document.addEventListener('myevent', log('A'));
document.dispatchEvent(new CustomEvent('myevent'));
setTimeout(log('B'), 0);
setTimeout(log('C'), 16);
requestAnimationFrame(log('D'));
Promise.resolve().then(log('E'));
window.addEventListener('message', log('F'));
window.postMessage({}, location.origin);

Тот случай, когда сложнее вспомнить, что является макро-задачей, что микро, а что ни тем ни другим

Имхо, это грязь. Чуваки не смогли придумать нормальный челлендж, и придумали этот.

Во первых, синхронная обработка события dispatchEvent - малоизвестный факт, которым почти не пользуются, кроме, может быть, каких-то исключительных кейсов (хотя да, в учебнике об этом есть). Во вторых, буквы B, D и F могут выскочить в разной последовательности, в зависимости от браузера и обстоятельств.

Sign up to leave a comment.

Articles