Əgər siz yalnız proqramlaşdırmaya başlayırsınızsa, çox güman ki, proqramları ardıcıl məntiq blokları toplusu kimi düşünürsünüz, burada hər blok müəyyən bir işi görür və nəticəsini növbəti blok işləyə bilsin və s. çox hissəsi haqlısınız, əksər proqramlar ardıcıl şəkildə işləyir, bu model bizə yazmaq və saxlamaq üçün sadə proqramlar qurmağa imkan verir. Bununla belə, bu ardıcıl modelin işləməyəcəyi və ya optimal olmadığı xüsusi istifadə halları var. Nümunə olaraq kitab oxuyucu proqramını nəzərdən keçirək. Bu proqram sözün bütün təkrarlarını tapmaq, əlfəcinlər arasında naviqasiya və buna bənzər bir neçə təkmil xüsusiyyətlərə malikdir. İndi təsəvvür edin ki, istifadəçi hazırda uzun bir kitab oxuyur və “The” kimi ümumi sözün bütün hallarını axtarmağa qərar verir. Tətbiq adətən həmin sözün bütün hallarını tapmaq və indeksləşdirmək üçün bir neçə saniyə çəkəcək. Ardıcıl proqramda istifadəçi axtarış əməliyyatı yerinə yetirilənə qədər proqramla əlaqə saxlaya bilməz (səhifəni dəyişdirə və ya mətni vurğulaya bilməz). Ümid edirik ki, bunun optimal istifadəçi təcrübəsi olmadığını görə bilərsiniz!
Diaqram kitab oxuyucu tətbiqinin tipik icra axınını göstərir. İstifadəçi uzun müddət davam edən əməliyyata başlayarsa (bu halda böyük bir kitabda “the” sözünün bütün hallarının axtarışı), proqram həmin əməliyyatın bütün müddəti ərzində “donur”. Bu halda, istifadəçi axtarış əməliyyatı başa çatana qədər heç bir nəticə vermədən növbəti əlfəcin düyməsini basmağa davam edəcək və bütün əməliyyatlar bir anda qüvvəyə minəcək və son istifadəçiyə geridə qalmış tətbiq hissini verəcək.
Bu nümunənin əvvəllər təqdim etdiyimiz ardıcıl modelə həqiqətən uyğun gəlmədiyini fərq etmiş ola bilərsiniz. Çünki buradakı əməliyyatlar bir-birindən müstəqildir. İstifadəçinin növbəti əlfəcinə keçmək üçün “the” baş vermələrinin sayını bilməyə ehtiyac yoxdur, buna görə də əməliyyatların icrası qaydası həqiqətən vacib deyil. Növbəti əlfəcinə keçməzdən əvvəl axtarış əməliyyatının bitməsini gözləməli deyilik. Əvvəlki icra axınında mümkün təkmilləşdirmə bu məntiqə əsaslanır: biz uzun axtarış əməliyyatını arxa planda icra edə, istənilən daxil olan əməliyyatları davam etdirə bilərik və uzun əməliyyat başa çatdıqdan sonra istifadəçini sadəcə xəbərdar edə bilərik. İcra axını aşağıdakı kimi olur:
Bu icra axını ilə istifadəçi təcrübəsi əhəmiyyətli dərəcədə yaxşılaşdırılır. İndi istifadəçi uzun müddət davam edən əməliyyata başlaya, proqramdan normal istifadə etməyə davam edə və əməliyyat başa çatdıqdan sonra xəbərdar ola bilər. Bu asinxron proqramlaşdırmanın əsasını təşkil edir.
Javascript, digər dillər arasında, düşünə biləcəyiniz hər hansı bir asinxron davranışa nail olmaq üçün geniş API təmin etməklə bu asinxron proqramlaşdırma tərzini dəstəkləyir. Günün sonunda Javascript mahiyyətcə asinxron dil olmalıdır. Əvvəlki nümunəyə istinad etsək, asinxron məntiq bütün istifadəçi qarşılıqlı tətbiqlərinin əsasını təşkil edir və Javascript əsasən proqramların əksəriyyətinin istifadəçi hərəkətlərinə cavab verməklə bağlı olduğu brauzerdə istifadə edilmək üçün qurulmuşdur.
Aşağıdakılar sizə Asinxron Javascript haqqında qısa bələdçi verəcək:
Geri Zənglər
Tipik bir proqramda adətən bir sıra funksiyaları tapa bilərsiniz. Funksiyadan istifadə etmək üçün onu parametrlər dəsti ilə çağırırıq. Funksiya kodu icra edəcək və nəticə qaytaracaq, qeyri-adi heç nə yoxdur. Asinxron proqramlaşdırma bu məntiqi bir qədər dəyişir. Kitab oxuyucu tətbiqi nümunəsinə qayıdaraq, əməliyyat naməlum vaxt tələb etdiyi üçün axtarış əməliyyatı məntiqini həyata keçirmək üçün müntəzəm funksiyadan istifadə edə bilmirik. Müntəzəm funksiya əsasən əməliyyatdan əvvəl qayıdacaq və bu bizim gözlədiyimiz davranış deyil. Həll yolu, axtarış əməliyyatı aparıldıqdan sonra yerinə yetiriləcək başqa bir funksiyanı təyin etməkdir. Proqramımız öz axınını normal şəkildə davam etdirə bildiyi üçün bu, istifadə vəziyyətimizi modelləşdirir və axtarış əməliyyatı başa çatdıqdan sonra göstərilən funksiya istifadəçini axtarış nəticələri barədə xəbərdar etmək üçün yerinə yetiriləcək. Bu funksiya geri çağırış funksiyası adlandırdığımız funksiyadır:
// 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);
Əvvəlcə axtarış əməliyyatı funksiyasını, searchOccurrences-i müəyyən edirik. Axtarmaq üçün söz və axtarış əməliyyatı tamamlandıqdan sonra yerinə yetiriləcək funksiya olacaq ikinci “geri zəng” parametri tələb olunur. Axtarış əməliyyatı funksiyası qəsdən mücərrəd saxlanılıb, biz yalnız onun iki mümkün nəticəsi üzərində fokuslanmalıyıq: birinci hal hər şeyin uğurlu getdiyi yerdir və biz nəticə dəyişənində axtarışın nəticəsini əldə edirik. Bu halda, sadəcə olaraq, geri çağırış funksiyasını aşağıdakı parametrlərlə çağırmalıyıq: birinci parametr sıfırdır, yəni heç bir xəta baş verməmişdir, ikinci parametr axtarılan sözdür, üçüncü və bəlkə də üç parametrin ən vacib parametridir., axtarış əməliyyatının nəticəsidir.
İkinci hal səhvin baş verdiyi yerdir, bu da axtarış əməliyyatının icrasının həyata keçirildiyi bir haldır və biz geri çağırış funksiyasını çağırmalıyıq. İstənilən xətanın qarşısını almaq üçün try and catch blokundan istifadə edirik və biz sadəcə olaraq catch blokundan xəta obyekti ilə geri çağırış funksiyasını çağırırıq.
Daha sonra biz geri çağırış funksiyasını, handleSearchOccurrences-i müəyyən etdik, onun məntiqini olduqca sadə saxladıq. Bu, sadəcə konsola mesaj çap etmək məsələsidir. Əsas funksiyada hər hansı bir səhvin olub olmadığını görmək üçün əvvəlcə “err” parametrini yoxlayırıq. Bu halda biz sadəcə istifadəçiyə məlumat veririk ki, axtarış əməliyyatı xəta ilə başa çatıb. Heç bir səhv qaldırılmadıqda, axtarış əməliyyatının nəticəsi ilə bir mesaj çap edirik.
Nəhayət, biz “the” sözü ilə searchOccurrences funksiyasını çağırırıq. Funksiya indi əsas proqramı bloklamadan normal şəkildə işləyəcək və axtarış tamamlandıqdan sonra geri çağırış yerinə yetiriləcək və biz axtarış nəticəsi və ya səhv mesajı ilə nəticə mesajını alacağıq.
Burada qeyd etmək vacibdir ki, bizim yalnız əsas və geri çağırış funksiyaları daxilində nəticə dəyişəninə çıxışımız var. Bu kimi bir şeyə cəhd etsək:
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);
proqramlar searchOccurrences funksiyasının yerinə yetirilməsini gözləmədiyi üçün çapın nəticəsi qeyri-müəyyən olacaq. Əsas funksiya daxilində nəticə dəyişəni təyin edilməzdən əvvəl çap ifadəsi olan növbəti təlimata keçir. Nəticədə təyin edilməmiş nəticə dəyişənini çap edəcəyik.
Beləliklə, bu məntiqə əsaslanaraq, nəticə dəyişənini istifadə edən bütün kodu geri çağırış funksiyasında saxlamalıyıq. Bu, indi problem kimi görünməsə də, tezliklə real problemə çevrilə bilər. Ardıcıllıqla işləməli olan asinxron funksiyalar zəncirinin olduğunu təsəvvür edin, tipik geri çağırış məntiqində belə bir şey həyata keçirəcəksiniz:
functionA(function (err, resA) {
///......
functionB(resA, function (err, resB) {
///......
functionC(resB, function (err, resC) {
///......
functionD(resC, function (err, resD) {
///......
});
});
});
});
Nəzərə alın ki, hər bir geri çağırışın səhv parametri var və hər bir səhv ayrıca idarə olunmalıdır. Bu, yuxarıda göstərilən onsuz da mürəkkəb kodu daha da mürəkkəb və saxlanmasını çətinləşdirir. İnşallah, Promises geri çağırış cəhənnəm problemini həll etmək üçün buradadır, biz bunu növbəti dəfə əhatə edəcəyik.
Vədlər
Vədlər geri çağırışlar üzərində qurulur və oxşar şəkildə fəaliyyət göstərir. Onlar ES6 xüsusiyyətlərinin bir hissəsi kimi geri çağırış cəhənnəmi kimi geri çağırışlarla bağlı bir neçə parlaq problemləri həll etmək üçün təqdim edildi. Vədlər müvəffəqiyyətlə tamamlandıqda (həll edilərkən) və səhvlər baş verdikdə (rədd edilir) işləyən öz funksiyalarını təmin edir. Aşağıda vədlərlə həyata keçirilən searchOccurrences nümunəsi göstərilir:
// 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`);
});
Tətbiq etdiyimiz dəyişikliklərə nəzər salaq:
SearchOccurrences funksiyası vəd qaytarır. Vəd daxilində biz eyni məntiqi saxlayırıq: həm uğurlu icranı, həm də səhvlərlə icranı idarə edən tək geri çağırış funksiyasına malik olmaqdansa, geri çağırışlarımızı təmsil edən həll edən və rədd edən iki funksiyamız var. Vədlər iki nəticəni ayırır və əsas funksiyanı çağırarkən təmiz sintaksis təmin edir. Həll funksiyası “sonra” açar sözündən istifadə edərək əsas funksiyaya “bağlıdır”. Burada sadəcə həll funksiyasının iki parametrini təyin edirik və axtarış nəticəsini çap edirik. Bənzər bir şey rədd etmə funksiyasına aiddir, onu "tutmaq" açar sözündən istifadə etməklə bağlamaq olar. Ümid edirik ki, kodun oxunaqlılığı və təmizliyi baxımından vəd etdiyi üstünlükləri qiymətləndirə bilərsiniz. Əgər hələ də bunu müzakirə edirsinizsə, bir-birinin ardınca işləmək üçün asinxron funksiyaları birləşdirərək geri çağırış cəhənnəm problemini necə həll edə biləcəyimizi yoxlayın:
searchOccurrences("the")
.then(searchOccurrences("asynchronous"))
.then(searchOccurrences("javascript"))
.then(searchOccurrences("guide"))
.catch((err) => {
console.log(`Search operation ended with an error`);
});
Async/Gözləyin
Async/Await Javascript-də asinxron alət kəmərimizə ən son əlavədir. ES8 ilə təqdim edilən onlar asinxron əməliyyatın icrasını sadəcə “gözləməklə” asinxron funksiyaların üstündə yeni abstraksiya qatını təmin edirlər. Proqramın hərəkəti asinxron əməliyyatdan nəticə qaytarılana qədər həmin təlimatda bloklanır və sonra proqram növbəti təlimatla davam edəcəkdir. Sinxron icra axını haqqında düşünürsünüzsə, düz deyirsiniz. Tam dövrəyə gəldik! Async/await sinxron proqramlaşdırmanın sadəliyini asinxron dünyaya gətirmək cəhdləri. Nəzərə alın ki, bu, yalnız proqramın icrası və kodunda qəbul edilir. Başlıq altında hər şey eyni qalır, Async/await hələ də vədlərdən istifadə edir və geri çağırışlar onların tikinti bloklarıdır.
Nümunəmizi nəzərdən keçirək və Async/await istifadə edərək həyata keçirək:
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}`);
Kodumuz çox dəyişmədi, burada diqqət yetirməli olan vacib şey searchOccurrences funksiyası bəyannaməsindən əvvəl “async” açar sözüdür. Bu, funksiyanın asinxron olduğunu göstərir. Həmçinin, searchOccurrences funksiyasını çağırarkən “gözləyin” açar sözünə diqqət yetirin. Bu, proqrama növbəti təlimata keçməzdən əvvəl nəticə qaytarılana qədər funksiyanın icrasını gözləməyi tapşıracaq, başqa sözlə, nəticə dəyişəni həmişə searchOccurrences funksiyasının qaytarılmış dəyərini saxlayacaq və vədini deyil. funksiyası, bu mənada, Async/Await-in Promises kimi gözlənilən vəziyyəti yoxdur. İcra başa çatdıqdan sonra biz çap ifadəsinə keçirik və bu dəfə nəticə əslində axtarış əməliyyatının nəticəsini ehtiva edir. Gözlənildiyi kimi, yeni kodun sinxron olduğu kimi eyni davranışı var.
Nəzərə almaq lazım olan başqa bir kiçik şey odur ki, bizdə artıq geri çağırış funksiyaları olmadığına görə, biz eyni funksiya daxilində searchOccurrences xətasını idarə etməliyik, çünki səhvi sadəcə geri çağırış funksiyasına çatdırıb orada idarə edə bilmərik. Burada misal üçün xəta baş verərsə, sadəcə olaraq səhv mesajını çap edirik.
Ümumiləşdirmə
Bu yazıda biz Javascript-də asinxron məntiqi həyata keçirmək üçün istifadə olunan müxtəlif yanaşmalardan keçdik. Biz niyə adi sinxron proqramlaşdırma tərzindən asinxron modelə keçməli olduğumuzun konkret nümunəsini araşdırmaqla başladıq. Daha sonra asinxron Javascript-in əsas tikinti blokları olan geri çağırışlara keçdik. Geri çağırışların məhdudiyyətləri bizi bu məhdudiyyətləri aradan qaldırmaq üçün illər ərzində əlavə edilmiş müxtəlif alternativlərə, əsasən vədlər və Async/gözləməyə apardı. Asinxron məntiq internetdə istənilən yerdə tapıla bilər, istər xarici API-yə zəng edirsinizsə, istər verilənlər bazası sorğusunu başlatırsınız, istər yerli fayl sisteminə yazırsınız, istərsə də giriş formasında istifadəçi daxiletməsini gözləyirsiniz. Ümid edirik ki, indi təmiz və davamlı Asinxron Javascript yazmaqla bu problemləri həll etmək üçün özünüzü daha inamlı hiss edirsiniz!
Əgər bu məqaləni bəyənirsinizsə, zəhmət olmasa CLA Blog-a baxın, burada texnologiyaya necə daxil olacağımız barədə müxtəlif mövzuları müzakirə edirik. Həmçinin, əvvəlki pulsuz seminarlarımız üçün youtube kanalımıza baxın və bizi sosial media vasitəsilə izləyin. -labs-academy/) qarşıdan gələnləri qaçırmamaq üçün!
Code Labs Academy-ın Web Development Bootcamp ilə HTML, CSS və JavaScript-də bacarıqlarını artırmaqla karyeranızı gələcək sübut edin.