برنامه نویسی asynchronous با جاوااسکریپت مدرن و async/await
برنامه نویسی asynchronous با Callback ها طول زیادی نکشید. در سال 2015 با Promise ها و در سال 2017 با async/await آشنا شدیم. اینجا دایکومنتیشن سایت رسمی ند جی اس را ترجمه میکنیم. در مقاله قبلی درباره پرامیس توضیحات کامل دادیم.
async/await ها با ادغام Promise ها و generator ها ساخته شدند. اساسا مفهومی بزرگتر از Promise ها دارند. اجازه دهید یکبار دیگر تکرار کنم: async/await بر مبنای پرامیس ساخته شده است.
چرا async/await معرفی شد؟
پرامیس ها در ایجاد زنجیره ها محدودیت داشتند. وقتی با آنها کار میکنید باید مراقب باشید زنجیره شکسته نشود. حالا با async/await اصلا زنجیره ای وجود ندارد اما همان کاربرد اجرا میشود.
از معرفی پرامیس فقط دو سال (از 2015 تا 2017) گذشته بود که راه حل async/await جایگزین آن شد. پرامیس آمده بود تا مشکل معروف callback hell را برای برنامه نویسی asynchronous حل کند.
وقتی به کدهای async/await نگاه میکنید، فکر میکنید کدها بصورت synchronous (همزمان) هستند درحالیکه انها در پشت صحنه بصورت asynchronous (ناهمزمان) هستند.
async/await چگونه به برنامه نویسی asynchronous کمک میکند؟
یک تابع که قبلش از async استفاده شده، یک پرامیس بازمیگرداند. مانند مثال زیر:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
وقتی میخواهید این تابع را فراخوانی کنید قبلش از await استفاده میکنید. حالا با حالت resolved یا rejected برمیگردد.
const doSomething = async () => {
console.log(await doSomethingAsync())
}
یک مثال کوچولو
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 3000)
})
}
const doSomething = async () => {
console.log(await doSomethingAsync())
}
console.log('Before')
doSomething()
console.log('After')
خروجی به این صورت خواهد بود:
Before
After
I did something
همه چیز را پرامیس کنید!
با قرار دادن عبارت async
قبل از یک فانکشن، در حقیقت دارید کاری میکنید که پرامیس برگرداند.
اگرچه به صراحت این پرامیس دیده نمیشود اما در داخل این اتفاق می افتد.
مقایسه دو قطعه کدهای زیر صحت این امر را مشخص میکنند:
const aFunction = async () => { return 'test' }
aFunction().then(alert)
کدهای بالا را با کدهای زیر مقایسه کنید! عملکرد یکسانی دارند.
const aFunction = () => { return Promise.resolve('test') }
aFunction().then(alert)
کدها خوانا تر هستند!
همانطور که در کدهای بالا مشاهده میکنید خوانایی بهتری نسبت به زنجیره های پرامیس دارند.
یک مثال ساده دیگر با هم اجرا میکنیم. در مثال های بزرگتر فواید و مزایا بهتر دیده میشوند.
مثلا اینجا با استفاده از پرامیس ما از یک منبع داده های جیسون میگیریم و ان ها را parse میکنیم:
const getFirstUserData = () => {
return fetch('/users.json') // get users list
.then(response => response.json()) // parse JSON
.then(users => users[0]) // pick first user
.then(user => fetch(`/users/${user.name}`)) // get user data
.then(userResponse => userResponse.json()) // parse JSON
}
getFirstUserData()
و کدهای زیر عملکرد یکسانی با کدهای بالا دارند با این تفاوت که از async/await نیرو گرفته اند:
const getFirstUserData = async () => {
const response = await fetch('/users.json') // get users list
const users = await response.json() // parse JSON
const user = users[0] // pick first user
const userResponse = await fetch(`/users/${user.name}`) // get user data
const userData = await userResponse.json() // parse JSON
return userData
}
getFirstUserData()
اجرای موازی چند فانکشن async
زنجیره های تابع async بسیار ساده بوجود می ایند و نسبت به زنجیره های پرامیس خوانا ترند.
const promiseToDoSomething = () => {
return new Promise(resolve => {
setTimeout(() => resolve('I did something'), 10000)
})
}
const watchOverSomeoneDoingSomething = async () => {
const something = await promiseToDoSomething()
return something + '\nand I watched'
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
const something = await watchOverSomeoneDoingSomething()
return something + '\nand I watched as well'
}
watchOverSomeoneWatchingSomeoneDoingSomething().then(res => {
console.log(res)
})
و خروجی خواهد بود:
I did something
and I watched
and I watched as well
دیباگ آسان تر
دیباگ پرامیس کار سختی است زیرا دیباگر در میان کدهای ناهمزمان گام به گام پیش نمیرود.
Async/await کار را ساده میکند زیرا از دید کامپایلر کد ها همزمان به نظر میرسند.