برنامهنویسی asynchronous روشی برای نوشتن کدی است که میتواند تسکها را به صورت مستقل از یکدیگر اجرا کند، بدون اینکه نیاز باشد یک تسک قبل از شروع تسک دیگر به پایان برسد. وقتی به برنامهنویسی asynchronous فکر میکنیم، مفهوم multitasking و مدیریت مؤثر زمان به ذهنمان میآید.
در این مقاله قصد داریم تا مفهوم async/await در تایپ اسکریپت و برنامهنویسی asynchronous را باهم بررسی کنیم.
قبل از ورود به بحث async/await، لازم است بدانیم که promiseها پایه و اساس برنامهنویسی asynchronous در تایپ اسکریپت و جاوااسکریپت هستند. یک promise نشاندهنده مقداری است که ممکن است بلافاصله در دسترس نباشد، اما در آینده resolve خواهد شد. یک promise میتواند در یکی از سه حالت زیر باشد:
در تایپ اسکریپت، میتوانیم promiseها را به شکل زیر ایجاد و استفاده کنیم:
// Type-safe Promise creation interface ApiResponse { data: string; timestamp: number; } const fetchData = new Promise<ApiResponse>((resolve, reject) => { try { // Simulating API call setTimeout(() => { resolve({ data: "Success!", timestamp: Date.now() }); }, ۱۰۰۰); } catch (error) { reject(error); } });
promiseها را میتوانیم با استفاده از .then()
برای عملیات موفق و .catch()
برای مدیریت خطا به صورت زنجیرهای اجرا نماییم:
fetchData .then(response => { console.log(response.data); // TypeScript knows response has ApiResponse type return response.timestamp; }) .then(timestamp => { console.log(new Date(timestamp).toISOString()); }) .catch(error => { console.error('Error:', error); });
در ادامه، مفهوم promiseها را دوباره بررسی خواهیم کرد و نحوه اجرای عملیات asynchronous به صورت parallel را توضیح خواهیم داد.
تایپ اسکریپت یک superset از جاوااسکریپت است، بنابراین async/await دقیقاً همانطور که در جاوااسکریپت کار میکند، در تایپ اسکریپت نیز عمل میکند؛ اما با ویژگیهای اضافی و type safety. تایپ اسکریپت به ما این امکان را میدهد که type safety را برای نتیجه مورد انتظار تضمین کنیم و حتی خطاهای تایپ را بررسی نماییم. این ویژگی به ما کمک میکند تا اشکالات را زودتر در فرآیند توسعه شناسایی کنیم.
async/await در واقع یک سینتکس سادهسازی برای promiseها است، یعنی کلمات کلیدی async و await در واقع یک لایه پوششی روی promiseها هستند. یک تابع async
همیشه یک promise را return میکند، حتی اگر مستقیماً از کلمه کلیدی Promise
استفاده نکنیم، کامپایلر آن را در یک resolve promise شده قرار میدهد.
به عنوان مثال:
//Snippet 1 const myAsynFunction = async (url: string): Promise<T> => { const { data } = await fetch(url) return data } //Snippet 2 const immediatelyResolvedPromise = (url: string) => { const resultPromise = new Promise((resolve, reject) => { resolve(fetch(url)) }) return resultPromise }
اگرچه این دو نمونه کد ظاهری متفاوت دارند، اما در اصل عملکرد آنها مشابه است.
async/await به ما این امکان را میدهد تا کدی که داریم را به صورت همگامتر بنویسیم و promise را در همان خط کد باز کنیم. این ویژگی هنگام کار با الگوهای پیچیده asynchronous بسیار مفید است.
برای استفاده درست و کامل از سینتکس async/await، داشتن درک پایهای از promiseها ضروری میباشد.
همانطور که قبلاً به آن اشاره کردیم، promise نشاندهنده این است که چیزی در یک زمان مشخص اتفاق خواهد افتاد، و این امکان را فراهم میکند که برنامه از نتیجه آن رویداد در آینده برای انجام کارهای دیگر استفاده کند.
برای توضیح بهتر، یک مثال واقعی را بررسی کرده و آن را به شبه کد و سپس کد تایپ اسکریپت تبدیل میکنیم.
فرض کنید ما یک چمنزار داریم که باید چمنهای آن را کوتاه کنیم. با یک شرکت تماس میگیریم که قول میدهد چمنها را در عرض چند ساعت کوتاه کند. در مقابل، ما نیز قول میدهیم که بلافاصله پس از کوتاه شدن چمنها هزینه را پرداخت نماییم، به شرطی که کار به درستی انجام شده باشد.
آیا الگوی خاصی را در این فرآیند مشاهده میکنیم؟ اولین نکته آشکار این است که رویداد دوم (پرداخت هزینه) کاملاً به رویداد اول (کوتاه شدن چمنها) وابسته است. اگر promise رویداد اول انجام شود، promise رویداد بعدی اجرا خواهد شد. در این مرحله، promise میتواند resolve شود، reject شود، یا در حالت pending باقی بماند.
در این کد، ما دو promise تعریف کردهایم: یکی که مربوط به شرکت است و دیگری مربوط به خودمان. promise شرکت بعد از ۱۰۰,۰۰۰ میلیثانیه یا resolve میشود یا reject میگردد. هر Promise
در یکی از سه وضعیت قرار دارد: resolved
(انجامشده) اگر مشکلی وجود نداشته باشد، rejected
(رد شده) اگر خطایی رخ دهد، یا pending
(در انتظار) اگر هنوز تصمیمگیری نشده باشد. در مثال ما، این وضعیت بعد از ۱۰۰۰۰۰
میلیثانیه مشخص خواهد شد.
// I send a request to the company. This is synchronous // company replies with a promise const angelMowersPromise = new Promise<string>((resolve, reject) => { // a resolved promise after certain hours setTimeout(() => { resolve('We finished mowing the lawn') }, ۱۰۰۰۰۰) // resolves after 100,000ms reject("We couldn't mow the lawn") }) const myPaymentPromise = new Promise<Record<string, number | string>>((resolve, reject) => { // a resolved promise with an object of 1000 Euro payment // and a thank you message setTimeout(() => { resolve({ amount: 1000, note: 'Thank You', }) }, ۱۰۰۰۰۰) // reject with 0 Euro and an unstatisfatory note reject({ amount: 0, note: 'Sorry Lawn was not properly Mowed', }) })
با استفاده از کلمه کلیدی then
میتوانیم promiseها را زنجیرهای اجرا کنیم. این روش مانند دستورات متوالی در زبان طبیعی است، «اول این کار را انجام بده، بعد آن کار و سپس … ».
کد زیر ابتدا angelMowersPromise
را اجرا میکند. در صورتی که خطایی وجود نداشته باشد، myPaymentPromise
اجرا میشود. در صورت بروز خطا در هر یک از این دو promise، آن خطا در بلاک catch
مدیریت خواهد شد:
angelMowersPromise .then(() => myPaymentPromise.then(res => console.log(res))) .catch(error => console.log(error))
یکی از کاربردهای رایج promiseها در برنامهنویسی فرانتاند، ارسال درخواستهای شبکهای و مدیریت پاسخها است. در کد زیر، یک درخواست برای دریافت لیست کارمندان از سرور ارسال شده است:
const api = 'http://dummy.restapiexample.com/api/v1/employees' fetch(api) .then(response => response.json()) .then(employees => employees.forEach(employee => console.log(employee.id)) // logs all employee id .catch(error => console.log(error.message))) // logs any error from the promise
گاهی اوقات نیاز داریم چندین promise را همزمان یا به ترتیب اجرا کنیم. در این شرایط، Promise.all
یا Promise.race
ابزارهای مفیدی هستند. به عنوان مثال، فرض کنید قصد داریم لیست ۱۰۰۰ کاربر GitHub را دریافت کرده و سپس برای هر کدام، یک درخواست جداگانه برای دریافت آواتار ارسال کنیم. در این حالت، انتظار برای هر درخواست به صورت ترتیبی منطقی نیست، بلکه بهتر است همه آنها را به طور همزمان اجرا نماییم. این موضوع را در بخش Promise.all
بررسی خواهیم کرد.
سینتکس async/await کار با promiseها را در جاوااسکریپت سادهتر میکند. این امکان را فراهم میکند که بتوانیم promiseها را به روشی خوانا و مشابه کدهای synchronous بنویسیم.
یک تابع async/await همیشه یک Promise
را return میکند. حتی اگر از کلمه کلیدی Promise
استفاده نکنیم، کامپایلر به طور خودکار تابع را درون یک Promise
resolve شده قرار میدهد. این ویژگی باعث میشود که مقدار بازگشتی از یک تابع async
را مانند یک Promise
در نظر بگیریم، که در مواقعی که نیاز به حل چندین عملیات asynchronous داریم، بسیار مفید است.
همانطور که از نام آن پیداست، async
همیشه در کنار await
استفاده میشود. به این معنی که فقط میتوانیم از await
درون یک تابع async
استفاده کنیم. وجود async
در ابتدای تابع به کامپایلر اطلاع میدهد که این یک تابع asynchronous است.
اگر بخواهیم promiseهای مثال بالا را به async/await تبدیل کنیم، سینتکس آن به شکل زیر خواهد بود:
const myAsync = async (): Promise<Record<string, number | string>> => { await angelMowersPromise const response = await myPaymentPromise return response }
همانطور که مشاهده میکنیم، این روش خواناتر است و اجرای کد حس همگام بودن دارد. در اینجا، await منتظر اجرای angelMowersPromise
میماند و سپس myPaymentPromise
اجرا میشود.
ما در این بخش، از مثال دریافت اطلاعات کارمند برای مشاهده نحوه مدیریت خطا استفاده میکنیم؛ زیرا این عملیات به دلیل درخواستهای شبکهای ممکن است با خطا مواجه شود.
برای مثال، فرض کنید که سرور از دسترس خارج شده یا درخواست ما به درستی ارسال نشده است. در چنین شرایطی، باید اجرای برنامه را متوقف کنیم تا از کرش کردن آن جلوگیری نماییم. سینتکس این کار به شکل زیر خواهد بود:
interface Employee { id: number employee_name: string employee_salary: number employee_age: number profile_image: string } const fetchEmployees = async (): Promise<Array<Employee> | string> => { const api = 'http://dummy.restapiexample.com/api/v1/employees' try { const response = await fetch(api) const { data } = await response.json() return data } catch (error) { if (error) { return error.message } } }
ما این تابع را به عنوان یک تابع async
تعریف کردهایم. مقدار بازگشتی را به گونهای در نظر گرفتهایم که یا یک آرایه از کارمندان باشد یا یک رشتهای از پیامهای خطا. بنابراین، تایپ promise در اینجا Promise<Array<Employee> | string>
خواهد بود.
درون بلاک try
کدهایی قرار میگیرند که انتظار داریم در شرایط عادی و بدون خطا اجرا شوند. در مقابل، بلاک catch
هر گونه خطایی که رخ دهد را دریافت و مدیریت میکند. در چنین حالتی، فقط مقدار ویژگی message
از آبجکت error
را return میکنیم.
نکته جالب اینجاست که هر خطایی که درون بلاک try
رخ دهد، بلافاصله throw شده و توسط بلاک catch
گرفته میشود. اگر این خطا مدیریت نشود، ممکن است باعث بروز خطاهای سخت برای دیباگ شده یا حتی کل برنامه را متوقف نماید.
در حالی که استفاده از بلاکهای سنتی try/catch
برای مدیریت خطاها در سطح لوکال کارآمد است، اما اگر بیش از حد مورد استفاده قرار بگیرد، میتواند کد را شلوغ کرده و منطق اصلی برنامه را تحت تأثیر قرار دهد. در چنین شرایطی، استفاده از توابع Higher-Order راهحل بهتری خواهد بود.
تابع Higher-Order تابعی است که یک یا چند تابع دیگر را به عنوان آرگومان دریافت میکند یا یک تابع را return کند. در زمینه مدیریت خطاها، میتوانیم از یک تابع Higher-Order برای wrapping یک تابع async استفاده کنیم تا خطاهای احتمالی را مدیریت کند. به این ترتیب، منطق try/catch
از هسته اصلی برنامه جدا میشود و کد خواناتر و تمیزتر خواهد بود.
ایده اصلی این است که یک تابع Wrapper ایجاد کنیم که یک تابع async/await
را همراه با آرگومانهای لازم دریافت کند. درون این Wrapper یک بلاک try/catch
پیادهسازی میکنیم تا مدیریت خطاها را به صورت مرکزی انجام دهد. این کار باعث میشود که کد ما خواناتر و نگهداری آن آسانتر شود.
برای درک بهتر این موضوع، مثال دریافت اطلاعات کارمندان را بررسی میکنیم:
// Async function to fetch employee data async function fetchEmployees(apiUrl: string): Promise<Employee[]> { const response = await fetch(apiUrl); const data = await response.json(); return data; } // Wrapped version of fetchEmployees using the higher-order function const safeFetchEmployees = (url: string) => handleAsyncErrors(fetchEmployees, url); // Example API URL const api = 'http://dummy.restapiexample.com/api/v1/employees'; // Using the wrapped function to fetch employees safeFetchEmployees(api) .then(data => { if (data) { console.log("Fetched employee data:", data); } else { console.log("Failed to fetch employee data."); } }) .catch(err => { // This catch block might be redundant, depending on your error handling strategy within the higher-order function console.error("Error in safeFetchEmployees:", err); });
در این مثال، تابع safeFetchEmployees
از تابع Higher-Order handleAsyncErrors
برای wrap کردن تابع fetchEmployees
اصلی استفاده میکند.
این روش به طور خودکار خطاهایی را که ممکن است در طول API call رخ دهد کنترل میکند، آنها را ثبت کرده و برای نشان دادن وضعیت خطا، null را برمیگرداند. سپس مصرفکننده safeFetchEmployees
میتواند بررسی کند که آیا مقدار بازگشتی تهی است یا خیر، تا تعیین کند که آیا عملیات موفقیتآمیز بوده یا خطایی رخ داده است.
همانطور که قبلاً به آن اشاره کردیم، گاهی اوقات نیاز داریم که promiseها به صورت همزمان اجرا شوند.
بیایید یک مثال از API کارمندان را بررسی کنیم. فرض کنید ابتدا باید همه کارمندان را دریافت کنیم، سپس نام آنها را استخراج کرده و در نهایت یک ایمیل از نامشان تولید نماییم. بدیهی است که باید این توابع را هم به صورت متوالی و هم به طور همزمان اجرا کنیم تا یکی باعث متوقف شدن دیگری نشود.
در این شرایط، میتوانیم از Promise.all
استفاده کنیم. طبق گفته Mozilla «Promise.all
معمولاً زمانی استفاده میشود که چندین عملیات asynchronous به طور همزمان اجرا شدهاند و ما میخواهیم منتظر بمانیم تا همهی آنها تکمیل شوند».
/employee
id
از هر کاربر، و دریافت اطلاعات هر کاربر: /employee/{id}
const baseApi = 'https://reqres.in/api/users?page=1' const userApi = 'https://reqres.in/api/user' const fetchAllEmployees = async (url: string): Promise<Employee[]> => { const response = await fetch(url) const { data } = await response.json() return data } const fetchEmployee = async (url: string, id: number): Promise<Record<string, string>> => { const response = await fetch(`${url}/${id}`) const { data } = await response.json() return data } const generateEmail = (name: string): string => { return `${name.split(' ').join('.')}@company.com` } const runAsyncFunctions = async () => { try { const employees = await fetchAllEmployees(baseApi) Promise.all( employees.map(async user => { const userName = await fetchEmployee(userApi, user.id) const emails = generateEmail(userName.name) return emails }) ) } catch (error) { console.log(error) } } runAsyncFunctions()
در کد بالا:
fetchEmployees
تمام کارمندان را از baseApi
دریافت میکند. ابتدا await
پاسخ را دریافت کرده، آن را به JSON تبدیل میکند و سپس دادههای پردازش شده را return میکند.fetchEmployee
اطلاعات یک کارمند خاص را دریافت میکند.generateEmail
بر اساس نام هر کارمند، یک ایمیل تولید میکند.runAsyncFunctions
ابتدا تمام کارمندان دریافت میشوند. سپس از Promise.all
برای دریافت اطلاعات هر کارمند و تولید ایمیل آنها به صورت همزمان استفاده میکنیم.نکتهی کلیدی این است که داخل تابع async، کدها به ترتیب و خطبهخط اجرا میشوند. اگر قبل از تکمیل یک promise، سعی کنیم دادهی آن را پردازش کنیم، دچار خطا خواهیم شد. در fetchEmployee
نیز همین مسئله صادق است؛ تنها اطلاعات یک کارمند را دریافت کرده و پردازش میکنیم.
اگر خطایی رخ دهد، این خطا از promise reject شده به Promise.all
منتقل میشود و در نهایت به یک استثنا تبدیل میگردد که در catch
مدیریت میشود.
Promise.all
زمانی مفید است که نیاز داشته باشیم همه promiseها successe شوند، اما در دنیای واقعی، ممکن است برخی عملیاتها موفق و برخی دیگر ناموفق باشند. به عنوان مثال، در سیستم مدیریت کارمندان، ممکن است بخواهیم چندین رکورد کارمندان را بهروزرسانی کنیم، اما برخی بهروزرسانیها به دلیل خطاهای اعتبارسنجی یا مشکلات شبکهای ناموفق باشند.
در این شرایط، Promise.allSettled
بسیار کاربرد دارد. برخلاف Promise.all
که در صورت شکست یک promise کل عملیات را متوقف میکند، Promise.allSettled
تا زمانی که همه promiseها اجرا شوند، منتظر میماند و نتیجه هر promise را (موفق یا ناموفق) گزارش میدهد. به عنوان مثال سیستم مدیریت کارکنان را با این روش بهروزرسانی میکنیم:
interface UpdateResult { id: number; success: boolean; message: string; } const updateEmployee = async (employee: Employee): Promise<UpdateResult> => { const api = `${userApi}/${employee.id}`; try { const response = await fetch(api, { method: 'PUT', body: JSON.stringify(employee), headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); return { id: employee.id, success: true, message: 'Update successful' }; } catch (error) { return { id: employee.id, success: false, message: error instanceof Error ? error.message : 'Update failed' }; } }; const bulkUpdateEmployees = async (employees: Employee[]) => { const updatePromises = employees.map(emp => updateEmployee(emp)); const results = await Promise.allSettled(updatePromises); // Process results and generate a report const summary = results.reduce((acc, result, index) => { if (result.status === 'fulfilled') { acc.successful.push(result.value); } else { acc.failed.push({ id: employees[index].id, error: result.reason }); } return acc; }, { successful: [] as UpdateResult[], failed: [] as Array<{id: number; error: any}> }); return summary; };
Promise.allSettled
را میتوانیم مانند یک مدیر پروژه در نظر بگیریم که چندین تسک را پیگیری میکند. برخلاف Promise.all
که در صورت شکست یک تسک کل فرآیند را متوقف میکند، این مدیر به کارهای دیگر ادامه داده و در پایان گزارشی از تمام تسکها (موفق و ناموفق) ارائه میدهد.
کاربردهای این روش عبارتند از:
گاهی نیاز داریم حجم زیادی از دادهها را که به صورت دستهای دریافت میشوند، پردازش کنیم. فرض کنید در حال export کردن دادههای کارمندان یک سیستم سازمانی بزرگ هستیم؛ احتمالاً هزاران رکورد داریم که برای جلوگیری از اشغال بیشازحد حافظه، در چندین بخش دریافت میشوند.
حلقه for await...of
برای این سناریو ایدهآل است. این حلقه به ما کمک میکند تا دادههای asynchronous را به صورت تکتک پردازش کنیم و کدی بهینه و خوانا داشته باشیم. به عنوان مثال در سیستم مدیریت کارکنان داریم:
interface PaginatedResponse<T> { data: T[]; nextPage?: string; } async function* fetchAllPages<T>( initialUrl: string, fetchPage: (url: string) => Promise<PaginatedResponse<T>> ): AsyncIterableIterator<T> { let currentUrl = initialUrl; while (currentUrl) { const response = await fetchPage(currentUrl); for (const item of response.data) { yield item; } currentUrl = response.nextPage || ''; } } // Usage with type safety async function processAllEmployee() { const fetchPage = async (url: string): Promise<PaginatedResponse<Employee>> => { const response = await fetch(url); return response.json(); }; try { for await (const employee of fetchAllPages('/api/employees', fetchPage)) { // Process each employee as they come in console.log(`Processing employee: ${employee.employee_name}`); await updateEmployeeAnalytics(employee); } } catch (error) { console.error('Failed to process employees:', error); } } function updateEmployeeAnalytics(employee: Employee) { /** custom logic */}
میتوانیم for await...of
را مانند یک نوار نقاله در کارخانه در نظر بگیریم. به جای اینکه صبر کنیم تا همه محصولات تولید شوند و سپس بستهبندی را شروع کنیم، هر محصول را به محض ورود پردازش میکنیم.
مزایای این روش عبارتند از:
ترکیب توابع Higher-Order با async/await، الگوهای قدرتمندی برای مدیریت عملیاتهای asynchronous ایجاد میکند.
در سیستم مدیریت کارمندان، معمولاً باید آرایهای از دادهها را به صورت asynchronous پردازش کنیم. متدهای آرایهای مانند map
، filter
و reduce
هنگام استفاده از async/await چالشهایی دارند که باید به درستی مدیریت شوند. به عنوان مثال:
// Async filter: Keep only active employees async function filterActiveEmployees(employees: Employee[]) { const checkResults = await Promise.all( employees.map(async (employee) => { const status = await checkEmployeeStatus(employee.id); return { employee, isActive: status === 'active' }; }) ); return checkResults .filter(result => result.isActive) .map(result => result.employee); } // Async reduce: Calculate total department salary async function calculateDepartmentSalary(employeeIds: number[]) { return await employeeIds.reduce(async (promisedTotal, id) => { const total = await promisedTotal; const employee = await fetchEmployeeDetails(id); return total + employee.salary; }, Promise.resolve(0)); // Initial value must be a Promise }
نکات مهمی که هنگام کار با async/await در تایپ اسکریپت و توابع آرایهای باید به آنها توجه داشته باشیم عبارت است از:
map
با عملیات async یک آرایه از promiseها تولید میکند که باید با Promise.all
مدیریت شوند.filter
نیاز به پردازش خاص دارد، زیرا نمیتوانیم مستقیماً نتیجه promise را به عنوان شرط فیلتر استفاده کنیم.reduce
هنگام کار با async باید به درستی مقدار تجمیع شده را مدیریت نماید.گاهی نیاز است توابع کمکی خاصی برای عملیاتهای انجام شده روی دادههای asynchronous ایجاد کنیم. میتوانیم توابع Higher-Orderای بسازیم که عملکردهای اضافی مانند کش کردن را به عملیاتهای async اضافه کنند.
بهعنوان مثال، کد زیر از تابع Higher-Order withCache
برای افزودن قابلیت کش به یک تابع async که دادهها را بر اساس id
دریافت میکند، استفاده مینماید. اگر همان id چندین بار در مدت پنج ثانیه درخواست شود، مقدار کش شده return میشود تا از درخواستهای غیرضروری به شبکه جلوگیری شود.
// Higher-order function for caching async results function withCache<T>( asyncFn: (id: number) => Promise<T>, ttlMs: number = 5000 ) { const cache = new Map<number, { data: T; timestamp: number }>(); return async (id: number): Promise<T> => { const cached = cache.get(id); const now = Date.now(); if (cached && now - cached.timestamp < ttlMs) { return cached.data; } const data = await asyncFn(id); cache.set(id, { data, timestamp: now }); return data; }; } // Usage example const cachedFetchEmployee = withCache(async (id: number) => { const response = await fetch(`${baseApi}/employee/${id}`); return response.json(); });
Awaited
یک تایپ کمکی است که عملکرد await
را در توابع async
مدلسازی میکند. این تایپ مقدار resolve یک promise را استخراج میکند و هرگونه لایههای تودرتوی promise را حذف مینماید.
Awaited
به ما کمک میکند تایپ مقداری را که پس از await دریافت میکنیم، مشخص نماییم. این کار باعث میشود که تایپ اسکریپت متوجه شود پس از await
دیگر با یک promise سروکار نداریم، بلکه با داده واقعی مواجه هستیم.
سینتکس بیسیک آن به صورت زیر میباشد:
type MyPromise = Promise<string>; type AwaitedType = Awaited<MyPromise>; // AwaitedType will be 'string'
Awaited
دقیقاً مدل then
در promiseها را شبیهسازی نمیکند، اما هنگام استفاده از then
در توابع async
میتواند مفید باشد. اگر از await
درون یک callback مربوط به then
استفاده کنیم، Awaited
به تعیین تایپ مقدار استخراج شده کمک میکند و نیاز به type annotation اضافی را کاهش میدهد.
async و await در تایپ اسکریپت به ما این امکان را میدهند که کد asynchronous را به صورتی بنویسیم که مانند کد synchronous به نظر برسد. این ویژگی، خوانایی و درک کد را بسیار بهبود میبخشد.
مفاهیم کلیدی که هنگام کار با async/await در تایپ اسکریپت باید به خاطر داشته باشیم عبارتند از:
Promise.all
برای اجرای چندین عملیات همزمان مفید است، اما در صورت شکست یکی، کل عملیات را متوقف میکند.Promise.allSettled
اجازه میدهد همه promiseها اجرا شوند و نتیجه موفق یا ناموفق آنها را دریافت کنیم.for await...of
برای پردازش جریانهای دادهای بزرگ به صورت بهینه و بلادرنگ مناسب است.map
, filter
, reduce
) باید توجه ویژهای به نحوهی مدیریت promiseها داشته باشیم.Awaited
به تایپ اسکریپت کمک میکند تایپ مقداری که پس از await
به دست میآید را به درستی درک کند.با تسلط بر این مفاهیم، میتوانیم عملیاتهای asynchronous را در تایپ اسکریپت بهصورت کارآمد و خوانا مدیریت نماییم.
۵۰ درصد تخفیف ویژه نوروز فرانت کست تا ۱۵ فروردین
کد تخفیف: spr