Ասինխրոն JavaScript-ի սկսնակների ուղեցույց
Թարմացվել է November 15, 20249 Րոպեներ կարդացեք
Եթե դուք միայն սկսում եք ծրագրավորումը, ապա հավանականությունը մեծ է, որ դուք մտածում եք ծրագրերի մասին որպես տրամաբանության հաջորդական բլոկների մի շարք, որտեղ յուրաքանչյուր բլոկ անում է որոշակի բան և փոխանցում է իր արդյունքը, որպեսզի հաջորդ բլոկը կարողանա աշխատել և այլն, և մեծ մասամբ դուք ճիշտ եք, ծրագրերի մեծ մասն աշխատում է հաջորդական եղանակով, այս մոդելը մեզ թույլ է տալիս ստեղծել այնպիսի ծրագրեր, որոնք հեշտ է գրել և պահպանել: Կան հատուկ օգտագործման դեպքեր, սակայն, երբ այս հաջորդական մոդելը չի աշխատի կամ օպտիմալ չի լինի: Որպես օրինակ, դիտարկեք գիրք ընթերցող հավելված: Այս հավելվածն ունի մի քանի առաջադեմ գործառույթներ, ինչպիսիք են բառի բոլոր երևույթները գտնելը, էջանիշների միջև նավարկելը և այլն: Հիմա պատկերացրեք, որ օգտատերը ներկայումս երկար գիրք է կարդում և որոշում է փնտրել ընդհանուր բառի բոլոր երևույթները, ինչպիսին է «The»: Հավելվածը սովորաբար տևում է մի քանի վայրկյան՝ գտնելու և ինդեքսավորելու այդ բառի բոլոր երևույթները: Հերթական ծրագրում օգտատերը չի կարող շփվել հավելվածի հետ (փոխել էջը կամ ընդգծել տեքստը), քանի դեռ որոնման գործողությունը չի կատարվել: Հուսով եմ, դուք կարող եք տեսնել, որ դա օպտիմալ օգտագործողի փորձ չէ:
Դիագրամը ցույց է տալիս գիրք ընթերցող հավելվածի տիպիկ կատարողական հոսքը: Եթե օգտատերը նախաձեռնում է երկարատև գործողություն (այս դեպքում մեծ գրքում «the»-ի բոլոր երևույթների որոնումը), ապա հավելվածը «սառեցնում է» այդ գործողության ողջ տևողության ընթացքում: Այս դեպքում օգտատերը կշարունակի սեղմել հաջորդ էջանիշի կոճակի վրա՝ առանց արդյունքի, մինչև որոնման գործողությունն ավարտվի, և բոլոր գործողությունները միանգամից ուժի մեջ կմտնեն՝ վերջնական օգտագործողին տալով ուշացած հավելվածի զգացում:
Դուք կարող եք նկատել, որ այս օրինակն իրականում չի համապատասխանում այն հաջորդական մոդելին, որը մենք ներկայացրել ենք ավելի վաղ: Դա պայմանավորված է նրանով, որ այստեղ գործողությունները միմյանցից անկախ են: Օգտագործողը կարիք չունի իմանալու «the»-ի դեպքերի քանակի մասին՝ հաջորդ էջանիշին նավարկելու համար, ուստի գործողությունների կատարման կարգն իսկապես կարևոր չէ: Մենք չպետք է սպասենք որոնման գործողության ավարտին, նախքան հաջորդ էջանիշին անցնելը: Նախորդ կատարման հոսքի հնարավոր բարելավումը հիմնված է այս տրամաբանության վրա. մենք կարող ենք երկար որոնման գործողությունը կատարել հետին պլանում, շարունակել ցանկացած մուտքային գործողություն, և երբ երկար գործողությունն ավարտվի, մենք կարող ենք պարզապես տեղեկացնել օգտվողին: Կատարման հոսքը դառնում է հետևյալը.
Այս կատարողական հոսքի շնորհիվ օգտվողի փորձը զգալիորեն բարելավվում է: Այժմ օգտատերը կարող է նախաձեռնել երկարատև գործողություն, շարունակել օգտագործել հավելվածը և ստանալ ծանուցում գործողությունն ավարտելուց հետո: Սա ասինխրոն ծրագրավորման հիմքն է։
Javascript-ը, ի թիվս այլ լեզուների, աջակցում է ասինխրոն ծրագրավորման այս ոճին՝ տրամադրելով ընդարձակ API-ներ՝ հասնելու գրեթե ցանկացած ասինխրոն վարքագծի, որի մասին կարող եք մտածել: Ի վերջո, Javascript-ը պետք է իր էությամբ ասինխրոն լեզու լինի: Եթե անդրադառնանք նախորդ օրինակին, ասինքրոն տրամաբանությունը օգտատիրոջ հետ փոխազդեցության բոլոր հավելվածների հիմքում է, և Javascript-ը հիմնականում ստեղծվել է բրաուզերի վրա օգտագործելու համար, որտեղ ծրագրերի մեծ մասը վերաբերում է օգտվողի գործողություններին արձագանքելուն:
Հետևյալը ձեզ կտրամադրի համառոտ ուղեցույց Asynchronous Javascript-ի վերաբերյալ.
Հետզանգեր
Տիպիկ ծրագրում դուք սովորաբար կգտնեք մի շարք գործառույթներ: Ֆունկցիան օգտագործելու համար մենք այն անվանում ենք մի շարք պարամետրերով: Գործառույթի կոդը կգործարկվի և կվերադարձնի արդյունք, ոչ մի արտասովոր բան: Ասինխրոն ծրագրավորումը մի փոքր փոխում է այս տրամաբանությունը: Վերադառնալով գիրք ընթերցող հավելվածի օրինակին, մենք չենք կարող սովորական ֆունկցիա օգտագործել որոնման գործողության տրամաբանությունն իրականացնելու համար, քանի որ գործողությունը անհայտ ժամանակ է պահանջում: Կանոնավոր գործառույթը հիմնականում կվերադառնա նախքան վիրահատությունը կատարելը, և դա այն վարքագիծը չէ, որը մենք ակնկալում ենք: Լուծումն այն է, որ նշվի մեկ այլ գործառույթ, որը կկատարվի որոնման գործողությունն ավարտելուց հետո: Սա մոդելավորում է մեր օգտագործման դեպքը, քանի որ մեր ծրագիրը կարող է նորմալ շարունակել իր հոսքը, և որոնողական գործողությունն ավարտվելուն պես, նշված գործառույթը կկատարվի՝ օգտվողին ծանուցելու որոնման արդյունքների մասին: Այս ֆունկցիան այն է, ինչ մենք անվանում ենք հետադարձ կապի գործառույթ.
// Search occurrences function
function searchOccurrences(word, callback) {
try {
// search operation logic, result is in result variable
//....
callback(null, word, result);
} catch (err) {
callback(err);
}
}
// Search occurrences callback function
function handleSearchOccurrencesResult(err, word, result) {
if (err) {
console.log(`Search operation for ${word} ended with an error`);
} else console.log(`Search results for ${word}: ${result}`);
return;
}
searchOccurrences("the", handleSearchOccurrencesResult);
Նախ սահմանում ենք որոնման գործողության ֆունկցիան՝ searchOccurrences: Որոնման համար անհրաժեշտ է բառը և երկրորդ պարամետրը «հետադարձ զանգ», որը կլինի այն գործառույթը, որը պետք է կատարվի որոնման գործողությունն ավարտելուց հետո: Որոնման գործողության գործառույթը միտումնավոր պահվել է վերացական, մենք միայն պետք է կենտրոնանանք դրա երկու հնարավոր արդյունքների վրա. առաջին դեպքն այն է, երբ ամեն ինչ հաջող է անցել, և մենք ունենք որոնման արդյունքը արդյունքի փոփոխականում: Այս դեպքում մենք պարզապես պետք է կանչենք հետադարձ կապ ֆունկցիան հետևյալ պարամետրերով. առաջին պարամետրը null է, ինչը նշանակում է, որ սխալ տեղի չի ունեցել, երկրորդ պարամետրը որոնված բառն է, և երրորդ և, հավանաբար, ամենակարևոր պարամետրը երեքից:, որոնողական գործողության արդյունքն է։
Երկրորդ դեպքն այն է, երբ տեղի է ունենում սխալ, սա նաև այն դեպքն է, երբ որոնման գործողության կատարումը կատարվում է, և մենք պետք է կանչենք հետ կանչելու գործառույթը։ Մենք օգտագործում ենք «փորձել և բռնել» բլոկը՝ ցանկացած սխալ ընդհատելու համար, և մենք պարզապես կանչում ենք «catch» ֆունկցիան «catch» բլոկի սխալի օբյեկտի հետ:
Այնուհետև մենք սահմանեցինք հետադարձ զանգի գործառույթը՝ handleSearchOccurrences, մենք բավականին պարզ էինք պահում դրա տրամաբանությունը: Խոսքը միայն վահանակի վրա հաղորդագրություն տպելու մասին է: Մենք նախ ստուգում ենք «err» պարամետրը, որպեսզի տեսնենք, թե արդյոք հիմնական գործառույթում որևէ սխալ տեղի է ունեցել: Այդ դեպքում մենք պարզապես օգտատերին հայտնում ենք, որ որոնման գործողությունն ավարտվել է սխալմամբ։ Եթե սխալներ չեն արձանագրվել, մենք տպում ենք հաղորդագրություն որոնման գործողության արդյունքով:
Ի վերջո, մենք անվանում ենք searchOccurrences ֆունկցիան «the» բառով: Ֆունկցիան այժմ կաշխատի նորմալ՝ առանց հիմնական ծրագիրը արգելափակելու, և որոնումն ավարտվելուն պես, հետ կանչը կկատարվի, և մենք կստանանք արդյունքի հաղորդագրությունը կամ որոնման արդյունքով կամ սխալի հաղորդագրությամբ:
Կարևոր է նշել այստեղ, որ մեզ հասանելի է միայն արդյունքի փոփոխականը հիմնական և հետադարձ կապի գործառույթների ներսում: Եթե փորձենք այսպիսի բան.
let result;
function searchOccurrences(word, callback) {
try {
// search operation logic, result is in searchResult variable
//....
result = searchResult;
callback(null, word, result);
} catch (err) {
callback(err);
}
}
searchOccurrences("the", handleSearchOccurrencesResult);
console.log(result);
տպման արդյունքը չի սահմանվի, քանի որ ծրագրերը չեն սպասում, որ կատարվի searchOccurrences ֆունկցիան: Այն տեղափոխվում է հաջորդ հրահանգը, որը տպագիր հայտարարություն է, նախքան հիմնական ֆունկցիայի ներսում արդյունքի փոփոխական նշանակելը: Արդյունքում մենք կտպենք չնշանակված արդյունքի փոփոխականը։
Այսպիսով, հիմնվելով այս տրամաբանության վրա, մենք պետք է պահենք բոլոր կոդը, որն օգտագործում է արդյունքի փոփոխականը հետ կանչելու ֆունկցիայի ներսում: Սա կարող է հիմա խնդիր չթվա, բայց այն կարող է արագ վերածվել իրական խնդրի: Պատկերացրեք այն դեպքը, երբ մենք ունենք ասինխրոն ֆունկցիաների շղթա, որոնք պետք է գործարկվեն հաջորդականությամբ, տիպիկ հետադարձման տրամաբանության մեջ դուք կիրականացնեիք նման բան.
functionA(function (err, resA) {
///......
functionB(resA, function (err, resB) {
///......
functionC(resB, function (err, resC) {
///......
functionD(resC, function (err, resD) {
///......
});
});
});
});
Հիշեք, որ յուրաքանչյուր հետադարձ զանգ ունի սխալի պարամետր, և յուրաքանչյուր սխալ պետք է կարգավորվի առանձին: Սա ավելի բարդ է դարձնում առանց այն էլ բարդ ծածկագիրը վերևում և դժվար է պահպանել: Հուսով ենք, որ Promises-ն այստեղ է, որպեսզի լուծի հետադարձ կապի դժոխքի խնդիրը, մենք դա կանդրադառնանք հաջորդիվ:
Խոստումներ
Խոստումները կառուցվում են հետադարձ զանգերի վրա և գործում են նույն ձևով: Դրանք ներկայացվել են որպես ES6 առանձնահատկությունների մաս՝ հետադարձ զանգերի հետ կապված մի քանի ակնառու խնդիրներ լուծելու համար, ինչպիսին է հետադարձ կապի դժոխքը: Խոստումները ապահովում են իրենց գործառույթները, որոնք գործում են հաջող ավարտի (լուծելու) և սխալների առաջացման դեպքում (մերժում): Հետևյալը ցույց է տալիս խոստումներով իրականացված SearchOccurrences օրինակը.
// Search occurrences function
function searchOccurrences(word) {
return new Promise((resolve, reject) => {
try {
// search operation logic, result is in result variable
//....
resolve(word, result);
} catch (err) {
reject(err);
}
});
}
searchOccurrences("the")
.then((word, result) => {
console.log(`Search results for ${word}: ${result}`);
})
.catch((err) => {
console.log(`Search operation ended with an error`);
});
Եկեք անցնենք մեր կիրառած փոփոխություններին.
SearchOccurrences ֆունկցիան խոստում է տալիս: Խոստման ներսում մենք պահպանում ենք նույն տրամաբանությունը. մենք ունենք լուծելու և մերժելու երկու գործառույթ, որոնք ներկայացնում են մեր հետադարձ զանգերը, այլ ոչ թե ունենանք հետադարձ զանգի մեկ գործառույթ, որը կարգավորում է և՛ հաջող կատարումը, և՛ սխալներով կատարումը: Խոստումները բաժանում են երկու արդյունքները և ապահովում են մաքուր շարահյուսություն հիմնական ֆունկցիան կանչելիս: Լուծման ֆունկցիան «կապված» է հիմնական ֆունկցիային՝ օգտագործելով «այնուհետև» հիմնաբառը: Այստեղ մենք պարզապես նշում ենք լուծման ֆունկցիայի երկու պարամետրը և տպում որոնման արդյունքը։ Նմանատիպ բան վերաբերում է մերժման գործառույթին, այն կարող է կեռվել՝ օգտագործելով «catch» հիմնաբառը: Հուսանք, դուք կարող եք գնահատել խոստումների առավելությունները՝ կոդերի ընթերցանության և մաքրության առումով: Եթե դեռ քննարկում եք դրա մասին, տեսեք, թե ինչպես կարող ենք լուծել հետադարձ կապի դժոխքի խնդիրը՝ շղթայելով ասինխրոն ֆունկցիաները՝ մեկը մյուսի հետևից գործելու համար:
searchOccurrences("the")
.then(searchOccurrences("asynchronous"))
.then(searchOccurrences("javascript"))
.then(searchOccurrences("guide"))
.catch((err) => {
console.log(`Search operation ended with an error`);
});
Async/Await
Async/Await-ը Javascript-ում մեր ասինխրոն գործիքագոտու վերջին լրացումն է: Ներդրված ES8-ով, դրանք ապահովում են աբստրակցիայի նոր շերտ ասինխրոն ֆունկցիաների վերևում՝ պարզապես «սպասելով» ասինխրոն գործողության կատարմանը: Ծրագրի հոսքը արգելափակվում է այդ հրահանգով, մինչև արդյունքը չվերադարձվի ասինխրոն գործողությունից, այնուհետև ծրագիրը կշարունակվի հաջորդ հրահանգով: Եթե դուք մտածում եք կատարման համաժամանակյա հոսքի մասին, ճիշտ եք: Մենք լրիվ շրջան ենք եկել։ Async/wait-ը փորձում է համաժամանակյա ծրագրավորման պարզությունը բերել ասինխրոն աշխարհ: Խնդրում ենք նկատի ունենալ, որ դա ընկալվում է միայն ծրագրի կատարման և կոդում: Ամեն ինչ նույնն է մնում գլխարկի տակ, Async/wait-ը դեռ օգտագործում է խոստումները, իսկ հետադարձ զանգերը դրանց կառուցման բլոկներն են:
Եկեք անցնենք մեր օրինակին և իրականացնենք այն՝ օգտագործելով Async/wait:
async function searchOccurrences(word) {
try {
// search operation logic, result is in result variable
//....
return result;
} catch (err) {
console.log(`Search operation for ${word} ended with an error`);
}
}
const word = "the";
const result = await searchOccurrences(word, handleSearchOccurrencesResult);
console.log(`Search results for ${word}: ${result}`);
Մեր կոդը առանձնապես չի փոխվել, այստեղ կարևոր է նկատել «async» հիմնաբառը նախքան searchOccurrences ֆունկցիայի հռչակագիրը: Սա ցույց է տալիս, որ ֆունկցիան ասինխրոն է: Նաև ուշադրություն դարձրեք «սպասում» բանալի բառին, երբ կանչում եք SearchOccurrences ֆունկցիան: Սա կհանձնարարի ծրագրին սպասել ֆունկցիայի կատարմանը, մինչև արդյունքը վերադարձվի, մինչև ծրագիրը կարողանա անցնել հաջորդ հրահանգին, այլ կերպ ասած, արդյունքի փոփոխականը միշտ կպահի searchOccurrences ֆունկցիայի վերադարձված արժեքը և ոչ թե խոստումը: գործառույթը, այդ առումով, Async/Await-ը չունի սպասող վիճակ՝ որպես Promises: Կատարումն ավարտվելուց հետո մենք տեղափոխվում ենք տպագիր հայտարարություն և այս անգամ արդյունքն իրականում պարունակում է որոնման գործողության արդյունքը: Ինչպես և սպասվում էր, նոր կոդը ունի նույն վարքագիծը, կարծես համաժամանակյա լիներ:
Մեկ այլ աննշան բան, որը պետք է հիշել, այն է, որ քանի որ մենք այլևս չունենք հետադարձ կապի գործառույթներ, մենք պետք է կարգավորենք searchOccurrences սխալը նույն ֆունկցիայի ներսում, քանի որ մենք չենք կարող պարզապես տարածել սխալը հետադարձման ֆունկցիայի վրա և կարգավորել այն այնտեղ: Այստեղ մենք պարզապես տպում ենք սխալի հաղորդագրություն՝ հանուն օրինակի։
Ամփոփում
Այս հոդվածում մենք անցանք Javascript-ում ասինխրոն տրամաբանության իրականացման համար օգտագործվող տարբեր մոտեցումների միջով: Մենք սկսեցինք ուսումնասիրելով կոնկրետ օրինակ, թե ինչու պետք է ծրագրավորման կանոնավոր համաժամանակյա ոճից անցնենք ասինխրոն մոդելի: Այնուհետև մենք անցանք հետադարձ զանգերին, որոնք ասինխրոն Javascript-ի հիմնական կառուցողական բլոկներն են: Հետ կանչերի սահմանափակումները մեզ հանգեցրին տարբեր այլընտրանքների, որոնք ավելացվել էին տարիների ընթացքում՝ հաղթահարելու այդ սահմանափակումները, հիմնականում խոստումները և Async/wait-ը: Ասինխրոն տրամաբանությունը կարելի է գտնել համացանցում ցանկացած վայրում՝ անկախ նրանից՝ դուք կանչում եք արտաքին API, նախաձեռնում եք տվյալների բազայի հարցում, գրում եք տեղական ֆայլային համակարգին կամ նույնիսկ սպասում եք օգտատիրոջ մուտքագրմանը մուտքի ձևի վրա: Հուսանք, այժմ դուք ավելի վստահ եք զգում լուծել այս խնդիրները՝ գրելով մաքուր և պահպանվող Asynchronous Javascript:
Եթե ձեզ դուր է գալիս այս հոդվածը, խնդրում ենք ստուգել CLA Blog, որտեղ մենք քննարկում ենք տարբեր թեմաներ, թե ինչպես մտնել տեխնոլոգիա: Նաև տեսեք մեր youtube.com ալիքը մեր նախորդ անվճար սեմինարների համար և հետևեք մեզ սոցիալական լրատվամիջոցներում -labs-academy/), որպեսզի բաց չթողնեք գալիքները:
Ապագայել ձեր կարիերան՝ կատարելագործելով HTML, CSS և JavaScript-ի հմտությունները Code Labs Academy-ի Web Development Bootcamp:
