Event Loop
Ինչպես է աշխատում JavaScript-ի Event Loop-ը
Երբ սկսում ենք սովորել JavaScript, ամեն ինչ կարծես հասկանալի է՝ մի ֆունկցիա կանչում ես, այն աշխատում է, հետո հաջորդը։ Բայց հետո գալիս է պահը, երբ գրում ես `setTimeout`, կամ `Promise`, կամ `fetch`, ու զգում ես՝ ինչ-որ բան այնպես չի․ ինչու՞ է կոդի հերթականությունը խախտվում։ Ահա հենց այստեղ է խաղի մեջ մտնում Event Loop-ը։
Այս հոդվածում կբացատրեմ՝ ի՞նչ է Event Loop-ը, ինչի՞ համար է պետք, ու ոնց է իրականում աշխատում կուլիսներում։ Ամեն ինչ՝ պարզ օրինակներով։
Երևակայիր, որ JavaScript-ը աշխատում է հերթով՝ մեկ գործողություն կատարելով։ Սա կոչվում է call stack։ Դա մի տեղ է, որտեղ դրվում են բոլոր այն հրահանգները, որոնք պետք է կատարվեն։
function greet() {
console.log("Բարև աշխարհ");
}
greet(); // Կկատարի greet ֆունկցիան
Սկզբում `greet()`-ը դրվում է stack-ի մեջ։ JavaScript-ը մտնում է ֆունկցիայի ներսը, կատարում է `console.log`, ու երբ ավարտվում է, դուրս է բերում ֆունկցիան stack-ից։ Ամեն ինչ հերթով է։
Մինչ այս պահը ամեն ինչ պարզ է։ Բայց երբ ասինխրոն գործողություն ես կատարում, օրինակ՝ `setTimeout`, ամեն ինչ մի քիչ այլ կերպ է։
console.log("Սկիզբ");
setTimeout(() => {
console.log("Timeout-ից հետո");
}, 1000);
console.log("Վերջ");
Դու կմտածես՝ «Լավ, էս կոդը կգնա հերթով, կտեսնեմ՝ Սկիզբ, հետո Timeout-ից հետո, հետո Վերջ»։ Բայց չէ, այն կտպի․
Սկիզբ Վերջ Timeout-ից հետո
Ինչո՞ւ։ Որովհետև `setTimeout`-ը ուղարկվում է ժամանակավոր մի «միջավայր»՝ որը մենք չենք տեսնում։ Սա կոչվում է Web APIs (կամ Node.js-ում՝ իր տարբերակը)։ Երբ 1 վայրկյանը անցնում է, այն ասում է՝ «Ես պատրաստ եմ», ու callback-ը դրվում է task queue-ի մեջ՝ սպասելով իր հերթին։
JavaScript-ը շարունակում է աշխատել միայն այն ժամանակ, երբ call stack-ը դատարկ է։ Ու երբ հերթը հասնում է, Event Loop-ը վերցնում է հերթի մեջ դրված callback-ը ու վերադարձնում է call stack, որպեսզի այն էլ աշխատի։
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
Ի՞նչ կլինի։ Արդյունքը կլինի՝
A C B
Թեև timeout-ը 0 է, այն չի նշանակում՝ անմիջապես կաշխատի։ Այն սպասում է call stack-ի ազատմանը։ Սա նշանակում է՝ JavaScript-ը **երբեք չի ընդհատում ընթացիկ աշխատանքը**, նույնիսկ եթե timeout-ը "0" է։
Promise-ները և async/await-ը պատկանում են microtask կոչվող աշխարհին։ Երբ կոդի մեջ օգտագործում ես Promise, այն իր callback-ը չի դնում նույն հերթի մեջ, ինչ setTimeout-ը։
console.log("Start");
Promise.resolve().then(() => {
console.log("Promise-ից հետո");
});
setTimeout(() => {
console.log("Timeout-ից հետո");
}, 0);
console.log("End");
Արդյունքը կլինի՝
Start End Promise-ից հետո Timeout-ից հետո
Ինչպես է սա աշխատում՝ JavaScript-ը ավարտում է սինխրոն կոդը (`Start` և `End`), հետո նայում է՝ microtask queue-ում կա՞ն գործողություններ։ Եթե այո, կատարում է դրանք (Promise-ը), ու միայն հետո գնում է դեպի timeout-ը։
Event Loop-ը մի բան է, որը միշտ շրջում է հետևյալ կերպ՝
Այսպիսով, JavaScript-ը կարողանում է միաժամանակ սպասել գործողություններին (օրինակ՝ սերվերից պատասխան), բայց չկանգնեցնել ամբողջ ծրագիրը։
JavaScript-ի Event Loop-ը գաղտնի մեխանիզմ է, որը թույլ է տալիս մեզ գրել ասինխրոն կոդ՝ առանց մուլտիթրեդինգի։ Հասկանալով call stack-ը, microtasks-ը ու task queue-ը, դու կկարողանաս կանխատեսել քո կոդի կատարումը, ու կգրես ավելի հստակ ու կայուն async ծրագրեր։
Եթե հիշես այս երեք կանոնները՝ միշտ կհասկանաս ինչ է կատարվում․