استفاده از قابلیت async-await در انگولار — به زبان ساده
با معرفی نسخه جدید انگولار دیگر لازم نیست در مورد promise بازگشتی از ()http نگرانی داشته باشیم. همچنان میتوانیم از async-await برای منطقهای مبتنی بر promise دیگر استفاده کنیم. Promise-ها و تابعهای callback اجزای اساسی نوشتن کد ناهمگام در جاوا اسکریپت محسوب میشوند. در اپلیکیشنهای انگولار میتوانیم از Rx.js برای بهرهگیری از قدرت Observables، Subject، BehaviorSubject و غیره برای نوشتن کد ناهمگام به روشی مناسب استفاده کنیم. با معرفی جدیدترین پیشنویس ECMA script، متوجه شدیم که جاوا اسکریپت شروع به پشتیبانی از قابلیت async-await کرده است. اگر با #C آشنایی قبلی داشته باشید، میدانید که قابلیت async-await از نسخه 5 این زبان پشتیبانی میشود.
async-await
بر اساس توضیح MDN:
زمانی که یک تابع async فراخوانی میشود یک promise بازگشت میدهد. هنگامی که تابع async مقداری بازگشت میدهد، promise با استفاده از مقدار بازگشتی به صورت resolved درمیآید. هنگامی که تابع async یک استثنا یا مقداری ارائه میکند، promise با مقدار ارائه شده reject میشود. یک تابع async میتواند شامل یک عبارت await باشد که اجرای تابع async را تعلیق میکند و منتظر میماند تا promise حل شده بازگشت یابد تا اجرای تابع async را از سر بگیرد و مقدار resolve شده را بازگشت دهد. به بیان ساده این فرصت را به دست میآوریم که یک کد ناهمگام را به روش همگام بنویسیم.
مثال 1
در این بخش یک مثال ساده را بررسی میکنیم. تابعی را تصور کنید که یک promise بازگشت میدهد که پس از دو ثانیه resolve میشود و مقدار بازگشتی بهصورت یک آرگومان ارسال میشود:
1resolveAfter2Seconds(x) {
2 return new Promise(resolve => {
3 setTimeout(() => {
4 resolve(x);
5 }, 2000);
6 });
7 }
با استفاده از promise-ها مقدار مورد نظر را با استفاده از تابع callback به نام then به دست میآوریم.
1 getValueWithPromise() {
2 this.resolveAfter2Seconds(20).then(value => {
3 console.log(`promise result: ${value}`);
4 });
5 console.log('I will not wait until promise is resolved');
6 }
در مثال فوق، ()console.log در خط 5 پیش از ()console.log در خط 3 اجرا میشود. این ماهیت اساسی promise است. اکنون کاربرد async-await را بررسی میکنیم.
1 getValueWithPromise() {
2 this.resolveAfter2Seconds(20).then(value => {
3 console.log(`promise result: ${value}`);
4 });
5 console.log('I will not wait until promise is resolved');
6 }
در کد فوق چند نکته وجود دارد که باید توجه کنیم:
- در خط 1 یک پیشوند async به تابع الحاق یافته است. در صورتی که تابع از کلیدواژه await استفاده شده باشد، نوشتن این پیشوند ضروری است.
- در خط 2 تابع callback به نام ()then. را پس از تابع promise فراخوانی نمیکنیم. به جای آن در ابتدای فراخوانی تابع یک کلیدواژه await میگذاریم. این کلیدواژه موجب میشود که بلوک بعدی کد اجرا نشود. این بدان معنی است که ()console.log در خط 3 تنها زمانی پرینت میشود که promise در خط 2 درست مانند یک فراخوانی تابع resolve شده باشد.
- از آنجا که از تایپ اسکریپت استفاده میکنیم باید مقدار بازگشتی را به نوع خاصی تبدیل کنیم که در اینجا <number> در خط 2 است.
مثال 2
در این مثال دو عدد را با رویکرد مبتنی بر promise با هم جمع میکنیم.
1addWithPromise() {
2 this.resolveAfter2Seconds(20).then(data1 => {
3 let result1 = <number>data1;
4 this.resolveAfter2Seconds(30).then(data2 => {
5 let result2 = <number>data2;
6 this.additionPromiseResult = result1 + result2;
7 console.log(`promise result: ${this.additionPromiseResult}`);
8 });
9 });
10 }
در اپلیکیشنهای واقعی، به طور معمول از ساختار کد promise-then تودرتو استفاده میکنیم. در زمانی که دو سطح از تودرتویی داشته باشیم، کدی مانند فوق پدید میآید. تصور کنید 7 یا 8 سطح از تودرتویی با متغیرها و عبارتهای مختلف وجود داشته باشد. اکنون از رویکرد مبتنی بر async استفاده میکنیم:
1 async addWithAsync() {
2 const result1 = <number>await this.resolveAfter2Seconds(20);
3 const result2 = <number>await this.resolveAfter2Seconds(30);
4 this.additionAsyncResult = result1 + result2;
5 console.log(`async result: ${this.additionAsyncResult}`);
6 }
کافی است سادگی کد را مقایسه کنید. هر دو رویکرد نتیجه مشابهی به دست میدهند، اما برحسب پایداری و نگهداری کد، رویکرد async-await بر رویکرد سنتی مبتنی بر promise ارجحیت دارد.
مصرف کردن REST APIهای HTTP
تا به اینجا مثالهای سادهای را بررسی کردیم. در اپلیکیشن انگولار میتوانیم دادههای REST را با استفاده از Http یا سرویس HttpClient به دست آوریم. به طور معمول متدهای ()Get() ،put() ،delete() ،post کلاس HttpClient مقدار <Observable<T بازگشت میدهند. این امر موجب میشود که بتوانیم آنها را از طریق متد subscribe یا با استفاده از عملگر ()toPromise از RxJs مصرف کنیم.
به دست آوردن نتیجه HttpClient با Observable
بسیاری از توسعهدهندگان انگولار از subscribe برای دریافت دادههای Http REST استفاده میکنند و نمیدانند که تفاوت آن با promise چیست. متد subscribe از شیء Observable است. زمانی که در چیزی مشترک میشویم، callback به نام subscribe در مواردی که دادههای جدیدی از سوی Observer ارائه شود، اجرا خواهد شد. این در حالی است که دستگیره callback به نام ()then مربوط به promise باید حداکثر یک بار اجرا شود. بنابراین تا زمانی که نیاز به مصرف مکرر دادهها نداشته باشید، نباید از subscribe استفاده کنید. به جای آن میتوانید از ()toPromise استفاده کنید. اگر متوجه مثال ارائه شده در مستندات رسمی انگولار شده باشید، کاربرد زیادی از toPromise شده است:
1getDataUsingSubscribe() {
2 this.httpClient.get<Employee>(this.url).subscribe(data => {
3 this.subscribeResult = data;
4 console.log('Subscribe executed.')
5 });
6 console.log('I will not wait until subscribe is executed..');
7 }
به دست آوردن نتیجه HttpClient با استفاده از toPromise
Rx.js عملگری به نام ()toPromise ارائه کرده است که میتواند برای تبدیل <Observeble<T به promise مورد استفاده قرار گیرد. زمانی که این تبدیل انجام یابد، بلوک then هر زمان که دادهای وجود داشته باشد اجرا خواهد شد.
1 getDataUsingPromise() {
2 this.httpClient.get<Employee>(this.url).toPromise().then(data => {
3 this.promiseResult = data;
4 console.log('Promise resolved.')
5 });
6 console.log('I will not wait until promise is resolved..');
7 }
به دست آوردن نتیجه HttpClient با استفاده از async-await
در الگوی async-await نیازی به اشتراک (subscribe) و یا toPromise نداریم. در این روش کد ساده و بدیهی است. خط 3 زمانی که دادهها از url واکشی شوند اجرا خواهد شد. toPromise به promise تبدیل میشود و این promise پس از قطعی شدن، دادهها را در متغیر عضو asyncResult ذخیره میکند.
1 async getAsyncData() {
2 this.asyncResult = await this.httpClient.get<Employee>(this.url).toPromise();
3 console.log('No issues, I will wait until promise is resolved..');
4 }
برنامهنویسی شرطی
در اغلب موارد اپلیکیشن نیاز دارد دادهها را از یک url بگیرد و از شرطی برای واکشی دادههای بعدی استفاده کند. روش انجام کار با استفاده از کد Promise به صورت زیر است:
1getConditionalDataUsingPromise() {
2 this.httpClient.get<Employee>(this.url).toPromise().then(data => {
3 console.log('First Promise resolved.')
4 if (data.id > 5) {
5 let anotherUrl = 'http://dummy.restapiexample.com/api/v1/employee/23';
6 this.httpClient.get<Employee>(anotherUrl).toPromise().then(data => {
7 this.conditionalPromiseResult = data;
8 console.log('Second Promise resolved.')
9 });
10 }
11 });
12 }
با استفاده از کد async-await روش انجام کار به صورت زیر خواهد بود:
1async getConditionalDataUsingAsync() {
2 let data = await this.httpClient.get<Employee>(this.url).toPromise();
3 if (data.id > 5) {
4 let anotherUrl = 'http://dummy.restapiexample.com/api/v1/employee/23';
5 this.conditionalAsyncResult = await this.httpClient.get<Employee>(anotherUrl).toPromise();
6 }
7 console.log('No issues, I will wait until promise is resolved..');
8 }
سخن پایانی
در این مقاله به بررسی رویکرد async-await در اپلیکیشنهای انگولار پرداختیم. متوجه شدیم که قابلیت async-await روشی بهتر برای نوشتن کد ناهمگام در اپلیکیشنهای انگولار ارائه میکند.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش مقدماتی AngularJS برای ساخت اپلیکیشن های تک صفحه ای
- اسکرول بی نهایت در اپلیکیشن های انگولار — از صفر تا صد
- Lazy Loading در انگولار — به زبان ساده
==