معرفی async/await جاوا اسکریپت در 10 دقیقه

دوشنبه 2 بهمن 1396

در این مقاله به معرفی و بررسی جدیدترین افزونه ی جاوااسکریپت به نام async/await می پردازیم که کار با توابع ناهمگام را بسیار آسان تر و قابل فهم تر کرده است.

معرفی async/await جاوا اسکریپت در 10 دقیقه

برای مدت طولانی توسعه دهندگان جاوا اسکریپت باید با به callback ها برای کار با کد های ناهمگام اعتماد می کردند.بنا براین اکثر ما سختی کار با callback و ترس از مواجه شدن با توابعی مثل این تابع را تجربه کرده ایم.

خوشبختانه بعد از مدتی Promise ها آمدند که هشدار های سازمان یافته تری برای callback ها ارائه دادند و اکثر جامعه به سرعت شروع به استفاده از آن کردند.

حال با جدیدترین افزونه ی Async/Await نوشتن کد های جاوا اسکریپت به مراتب بهتر شده است.

Async/Await چیست؟

Async/Await یک ویژگی پیش بینی شده ی جاوااسکریپت است که کار با توابع ناهمگام را بسیار لذت بخش تر و قابل فهم تر کرده است. Async/Await برمبنای Promise ها ساخته شده است و با تمام API های موجود که پایه ی آن ها  Promise هاست سازگار است.

نام Async/Await از async و await تشکیل شده است این دو کلمه ی کلیدی به ما کمک می کند تا کد های ناهمگام را مرتب کنیم.

Async تابع ناهمگام تعریف می کند.

بطور خودکار یک تابع منظم (با قاعده ) را به Promis تبدیل می کند.

زمانی که توابع async  فراخوانی می شوند با هر آنچه که در بدنه ی آن ها تعریف شده بازگردانده می شوند

تابع های async استفاده از await را ممکن می کند.

Await اجرای توایع async را متوقف می کند.

 زمانی که await بعد از یک فراخوانی Promise قرار می گیرد بقیه ی کد هارا منتظر نگه می دارد تا Promise تمام شود و یک مقدار برگرداند.

Await فقط با Promis ها کار میکند نه با callback ها

Await فقط می تواند داخل توابع async استفاده شود.

در ادامه یک مثال ساده زده ایم که همه چیز را روشن می کند:

فرض کنید که می خواهیم یک سری فایل JSON از سرورمان دریافت کنیم بنابراین یک تابع می نویسیم که از کتابخانه axios استفاده می کند و یک درخواست HTTP GET به https://tutorialzine.com/misc/files/example.json میفرستد. حال ما باید منتظر پاسخ سرور باشیم بنابراین طبیعتا این درخواستHTTP، ناهمگام است.

در قطعه کد زیر تابع مشابهی دوبار استفاده شده است بار اول باPromise ها و بار دوم با Async/Await

// Promise approach

function getJSON(){

    // To make the function blocking we manually create a Promise.
    return new Promise( function(resolve) {
        axios.get('https://tutorialzine.com/misc/files/example.json')
            .then( function(json) {

                // The data from the request is available in a .then block
                // We return the result using resolve.
                resolve(json);
            });
    });

}

// Async/Await approach

// The async keyword will automatically create a new Promise and return it.
async function getJSONAsync(){

    // The await keyword saves us from having to write a .then() block.
    let json = await axios.get('https://tutorialzine.com/misc/files/example.json');

    // The result of the GET request is available in the json variable.
    // We return it just like in a regular synchronous function.
    return json;
}

کاملا واضح است که کدی که با Async/Await است بسیار کوتاه تر و قابل فهم تر است. جدا از syntax استفاده شده ، هردو تابع کاملا یکسان هستند هردو Promise برمیگردانند و هردو از پاسخ JSON از axios استفاده می کنند.ما می توانیم تابع async را مانند زیر فراخوانی کنیم:

getJSONAsync().then( function(result) {
    // Do something with result.
});

آیا Async/Await باعث از بین رفتن Promise ها می شود؟

نه.به هیچ وجه. هنگامی که ما داریم از Async/Await استفاده می کنیم درواقع داریم با واسطه از Promiseها استفاده می کنیم. فهم درست از Promise ها بسیار کمک می کند و به شدت توصیه می شود.

حتی برای مواردی که Async/Await کافی نیست و بایداز Promise ها کمک بگیریم use case های خاصی وجود دارد. یکی از این قبیل سناریو ها زمانی است که می خواهیم چندین فراخوانی مستقل نا همگام ایجاد کنیم و منتظر تمام شدن آن ها بمانیم.

اگر این کار را با async و await انجام دهیم نتایج زیر را خواهیم داشت:

async function getABC() {
  let A = await getValueA(); // getValueA takes 2 second to finish
  let B = await getValueB(); // getValueB takes 4 second to finish
  let C = await getValueC(); // getValueC takes 3 second to finish

  return A*B*C;
}

هر فراخوانی await منتظر نتیجه فراخوانی قبلی می ماند. ازآنجایی که در یک زمان فقط یک فراخوانی انجام می دهیم انجام تمام توابع 9 ثانیه طول خواهد کشید(2+4+3).از آنجایی که سه متغیر A و B و C به یکدیگر وابسته نیستند این راه حل بهینه نیست. به بیان دیگر ما نیاز نداریم قبل از گرفتن B مقدار A را بدانیم ما میتوانیم متغیر ها را همزمان دریافت کنیم و کمتر منتظر بمانیم.

برای ارسال همزمان تمام درخواست ها Promis.all() نیاز داریم این به ما این اطمینان را می دهد که تمام نتایج را تا قبل از اجرای کامل داریم اما فراخوانی های ناهمگام به طور موازی و نه یکی پس از دیگری انجام می شوند.

async function getABC() {
  // Promise.all() allows us to send all requests at the same time. 
  let results = await Promise.all([ getValueA, getValueB, getValueC ]); 

  return results.reduce((total,value) => total * value);
}

با این روش اجرای توابع زمان بسیار کمتری می برند. فراخوانی های getValueA  و  getValueC تا زمانی که getValueB تمام شود به پایان رسیده است. به جای جمع زمان های اجرا، زمان اجرا به طور چشم گیری کاهش می یابد(getValueB،4 ثانیه)

مدیریت ارورها در Async/Await

 یکی دیگر از ویژگی های جالب Async/Await این است که به ما اجازه می دهد که هر ارور غیر منتظره ای را در ساختار قدیمی و کاربردی try/catch قرار دهیم.

async function doSomethingAsync(){
    try {
        // This async call may fail.
        let result = await someAsyncCall();
    }
    catch(error) {
        // If it does we will catch the error here.
    }  
}

Catch ارور هایی که به وسیله ی فراخوانی های ناهمگام await یا هر کد دیگری که در بدنه ی try نوشته شده است را مدیریت می کند.

اگر نیاز باشد می توانیم ارور ها را هنگام اجرای تابع async هم بگیریم از آنجایی که تمام توابع async، Promise برمیگردانند به سادگی می توانیم هنگام فراخوانی آن ها از event handler ی به نام.catch() استفاده کنیم.

// Async function without a try/catch block.
async function doSomethingAsync(){
    // This async call may fail.
    let result = await someAsyncCall();
    return result;  
}

// We catch the error upon calling the function.
doSomethingAsync().
    .then(successHandler)
    .catch(errorHandler);

این مهم است که از چه متدی برای مدیریت ارور ها استفاده میکنیم. استفاده ی همزمان از try/catch و .catch منجر به مشکلاتی می شود.

پشتیبانی مرورگر ها

Async/Await درحال حاضر در اغلب مرورگر های اصلی موجود است. به جز IE11 همه ی مرورگر های دیگر کد های Async/Await را بدون هیچ کتابخانه ی اضافه تشخیص می دهد.

توسعه دهندگان Node از نسخه های بهبود یافته ی async تا زمانی که روی Node 8 یا نسخه های جدید تر کار میکنند استقبال می کنند. این موضوع باید به LTS در آینده تبدیل شود.

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید