Привіт усім! В цій статті я розповім, як оперувати виконанням коду на більш нижчому рівні. Якщо ви розумієте, що у вашому коді є макро- і мікрозадачі, то ви можете більше передбачити поведінку коду і послідовність виконання певних функцій та методів, що значно полегшує роботу в оптимізації вашого додатку.
Макро: setTimeout, setImmediate, setInterval, I/O, UI rendering.
Мікро: Promise, process.nextTick, queueMicrotask, і на фронті ми маємо наглядача за DOM елементами — MutationObserver.
Різниця між ними в тому, що мікрозадачі мають пріоритет перед макрозадачами.
Вони виконуються в першу чергу, лише після того, як виконуються всі мікрозадачі. Event loop
переходить до черги макрозадач — і потім знову після макрозадачі виконуються всі мікро-. І так по колу, поки Event queue
не стане порожнім.
У нас є черга подій Event queue
, в якій знаходяться всі наші події з їхніми обробниками в черзі. Event loop
обробляє всі події по принципу FIFO (first in first out) — тобто остання подія буде оброблена в останню чергу — все як за етикетом 🙂
Проте у нас є можливість піти поза чергою, використати, так би мовити, VIP-перепустку і обійти зареєстровані події 😉 Ми можемо це зробити завдяки декільком методам:
1. process.nextTick()
2. queueMicrotask()
3. setImmediate()
Зараз пропоную вам детально розглянути код, який наочно покаже, яка функція виконується і в якій послідовності, не дивлячись на те, де вона розташована і скільки часу потрібно для її виконання:
const fibonacci = n => { if(n <= 1){ return n; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } const loging = (...args) => { const [ colorKey, text, fibNumber ] = args; const colors = { y:'x1b[33m%sx1b[0m', b:'x1b[34m%sx1b[0m', w:'x1b[37m%sx1b[0m', }; const color = colors[colorKey] || colors['w']; console.log(color, text + ' ' + fibonacci(fibNumber)); } const task = async(a) => { const task2 = (t) =>Promise.resolve(t()); return await task2(fibonacci.bind(null, a)); } const taskContainer = () => { console.log('x1b[32m%sx1b[0m', '--- START taskContainer ---'); setImmediate(() =>loging('y', '2 -- setImmediate', 15)); // not regular execution setTimeout(() =>loging('y', '2 -- setTimeout', 15)); // not regular execution queueMicrotask(() =>loging('y', '2 -- queueMicrotask', 20)); process.nextTick(() =>loging('y', '2 -- nextTick', 35)); Promise.resolve().then(_=>loging('y', '2 -- Promise', 12)); console.log('x1b[31m%sx1b[0m', '--- END taskContainer ---'); }; setImmediate(() => loging('b', '1 -- setImmediate', 15)); // not regular execution setTimeout(() => loging('b', '1 -- setTimeout', 15)); // not regular execution task(20).then(res => console.log('Nested task result', res)); Promise.resolve().then(_ => loging('b', '1 -- Promise', 32)); queueMicrotask(() => loging('b', '1 -- queueMicrotask', 20)); process.nextTick(() => loging('b', '1 -- nextTick', 35)); console.log('x1b[34m%sx1b[0m', `1 -- log ${fibonacci(10)}`); taskContainer();
Результат:
Console output: 1 -- log 55 --- START taskContainer --- --- END taskContainer --- 1 -- nextTick 9227465 2 -- nextTick 9227465 1 -- Promise 2178309 1 -- queueMicrotask 6765 2 -- queueMicrotask 6765 2 -- Promise 144 Nested task result 6765 1 -- setTimeout 610 2 -- setTimeout 610 1 -- setImmediate 610 2 -- setImmediate 610
Розберемо отриманий результат:
console.log()
— завжди виконуються першими, тому що це теж I/O-операція і вона — завжди перша після ініціалізації коду. Це відбувається через те, що таймери виконуються після того, як будуть назначені — їх поведінку контролює poll-фаза, а до next tick queue
черга ще не дійшла.process.nextTick()
— виконується другим, тому що він спрацьовує в next tick queue
.queueMicrotask()
є альтернативою process.nextTick()
і виконується в тій самій черзі, де і Promise
, тому виконується завжди після process.nextTick()
. Але з Promise
виконуються на рівних правах, тобто залежно лише від послідовності в коді.setTimeout/setImmediate()
— це таймери, вони є макрозадачами, завжди виконуються після мінімальної затримки, якщо вона не вказана або, як ми знаємо, після всіх мікрозадач.Мікрозадачі ми використовуємо для того, щоб виконати асинхронну роботу коду. Це є надважливим, коли ми, наприклад, хочемо виконати функцію після ініціалізації всього коду даного файла, але до того, як весь код починає виконуватись.
Виключенням можуть бути деякі I/O-операції, вони виконуються синхроно одразу після ініціалізації, але перед next tick queue
. У всіх інших випадках краще використовувати макрозадачі, тому що їх поведінка більш передбачувана.
const importantObject = { _name:'Vladyslav' } process.nextTick(() => { console.log('My name is ', importantObject.getName()); }); importantObject.getName = function () { return this._name }
Результат:
Console output: `My name is Vladyslav`
На прикладі цього коду ми можемо побачити, як ми відклали виконання методу importantObject.getName()
і виведення його результату в консоль завдяки process.nextTick()
. Таким чином ми зачекали його ініціалізацію.
const importantObject = { _name:'Vladyslav' } console.log('My name is ', importantObject.getName()); importantObject.getName = function () { return this._name }
Результат:
Console output: `TypeError: importantObject.getName is not a function`
Без process.nextTick()
ми отримаємо помилку, бо викличемо той метод якого ще не матимемо в importantObject
.
Отже, ми розглянули детально мікро- і макрозадачі. І тепер кожен, прочитавши цю статтю і протестуючи код в прикладах, зможе чітко відповісти на питання стосовно послідовності виконання різних задач.
За що вам дякую і бажаю всім продуктивного кодування 😉
Днями я завзято нила про щось ChatGPT (експериментую між сеансами з живим терапевтом). І от…
«Крутіть колесо, щоб отримати знижку до 50%!» «Натисніть тут, щоб відкрити таємничу пропозицію!» «Зареєструйтесь зараз,…
Дуже хочеться робити якісь десктопні апки. Сумую за часами коли всі програми були offline-first, і…
Надсилаючи криптовалюту, багато новачків ставлять запитання: як працюють комісії та чому вони відрізняються в різних…
Нова афера набирає обертів — ось детальний розбір того, як фальшиві потенційні роботодавці намагаються вкрасти…
Соцмережа з можливістю вбудовувати повноцінні додатки прямо в пости — звучить як фантастика, але Farcaster…