async — Coroutine Builder در کاتلین که یک همروال را راهاندازی میکند و یک شی Deferred<T> برای دریافت نتیجه عملیات ناهمگام برمیگرداند. async امکان اجرای موازی چندین وظیفه و تجمیع نتایج آنها را از طریق await() فراهم میکند. بر اساس راهنمای JetBrains، 2024، async روش ترجیحی برای سازماندهی محاسبات موازی در همروالها است.
نکات اصلی
async — یک تابع توسعهدهنده CoroutineScope است که یک همروال با قابلیت بازگرداندن نتیجه ایجاد میکند. برخلاف launch، async یک Deferred<T> — شیئی که یک مقدار آینده را نشان میدهد — برمیگرداند. همروال راهاندازی شده از طریق async به صورت همروند با سایر همروالها اجرا میشود که امکان موازیسازی محاسبات را فراهم میکند.
async زمانی استفاده میشود که نتیجه عملیات ناهمگام برای پردازش بعدی مورد نیاز است: بارگذاری داده از چندین منبع، پردازش دستهای مجموعهها، درخواستهای موازی به APIهای مختلف. Deferred بلافاصله ایجاد میشود و نتیجه پس از اتمام همروال در دسترس میشود.
import kotlinx.coroutines.*
suspend fun String.delayedValue(): String {
delay(1000L)
return this
}
fun main() = runBlocking {
val deferred: Deferred<String> = async { "Hello".delayedValue() }
println(deferred.await())
}
امضای async مشابه launch است: همان پارامترهای CoroutineContext، CoroutineStart و بلوک suspend. تفاوت در نوع بازگشتی است — Deferred<T> به جای Job، که در آن T نوع نتیجهای است که توسط آخرین عبارت بلوک بازگردانده میشود.
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — واسطی که از Job ارثبری میکند و متدهای await()، getCompleted()، getCompletionExceptionOrNull() را اضافه میکند. await() یک تابع suspend است و فقط درون یک همروال یا تابع suspend دیگر قابل فراخوانی است. getCompleted() — یک متد غیرمسدودکننده که اگر همروال هنوز کامل نشده باشد استثنا پرتاب میکند.
مزیت اصلی async امکان راهاندازی موازی وظایف است. به جای انتظار ترتیبی برای هر عملیات (زمان کل = مجموع زمانها)، async همروالها را همزمان راهاندازی میکند و زمان کل را به زمان طولانیترین عملیات کاهش میدهد.
سناریوی معمول — بارگذاری پروفایل کاربر، تنظیمات و تاریخچه سفارشات با سه درخواست async موازی. پس از اتمام هر سه، نتایج در یک مدل داده واحد ترکیب میشوند.
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
val profile = async { api.getProfile(userId) }
val settings = async { api.getSettings(userId) }
val orders = async { api.getRecentOrders(userId) }
Dashboard(
profile = profile.await(),
settings = settings.await(),
orders = orders.await()
)
}
تابع coroutineScope همروالهای فرزند را راهاندازی میکند و قبل از بازگرداندن نتیجه منتظر اتمام آنها میماند. کل بلوک به صورت موازی اجرا میشود، نه ترتیبی.
Deferred چندین روش برای کار با نتیجه پشتیبانی میکند. علاوه بر await()، روشهایی برای بررسی وضعیت، دریافت مقدار آماده و تجمیع چندین Deferred از طریق توابع کمکی کاتلین کوروتینز در دسترس است.
| متد | توضیحات | Suspend؟ |
|---|---|---|
| await() | همروال را تا دریافت نتیجه متوقف میکند | بله |
| getCompleted() | نتیجه را برمیگرداند اگر همروال کامل شده باشد (در غیر این صورت Exception) | خیر |
| getCompletionExceptionOrNull() | در صورت خطا استثنا یا null را برمیگرداند | خیر |
| isCompleted | بررسی میکند که آیا همروال کامل شده است | خیر |
برای تجمیع لیست Deferred از awaitAll() استفاده میشود — تابعی که اجرا را تا اتمام تمام Deferredهای موجود در مجموعه متوقف میکند. اگر حداقل یکی با خطا به پایان رسیده باشد، awaitAll() استثنا پرتاب میکند.
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
انتخاب بین async و launch به نیاز به نتیجه اجرای همروال بستگی دارد. launch Job را برمیگرداند و برای عملیات fire-and-forget مناسب است، async Deferred را برمیگرداند و برای وظایفی که داده برمیگردانند استفاده میشود. از نظر نحو و پارامترها، سازندهها یکسان هستند.
قانون مهم: هرگز از async برای fire-and-forget استفاده نکنید. اگر نتیجه همروال مورد نیاز نیست، از launch استفاده کنید. async سربار ایجاد Deferred را تحمیل میکند که در چنین سناریویی توجیهپذیر نیست.
در اندروید async برای عملیات موازی داخل ViewModel و UseCases استفاده میشود. سناریوهای معمول: بارگذاری همزمان پروفایل و فید خبری، دریافت آب و هوا از چندین سرویس هواشناسی، همگامسازی دستهای داده با سرور.
class HomeViewModel : ViewModel() {
fun loadHomeScreen() {
viewModelScope.launch {
val userData = async(Dispatchers.IO) { repository.getUserData() }
val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
val notifications = async(Dispatchers.IO) { repository.getNotifications() }
val state = HomeState(
user = userData.await(),
news = newsFeed.await(),
unread = notifications.await()
)
_uiState.update { state }
}
}
}
توصیه میشود گروه async را در coroutineScope یا supervisorScope برای جداسازی خطاها بپیچید. supervisorScope امکان ادامه اجرای سایر همروالهای async را حتی در صورت خطا در یکی از آنها فراهم میکند — این برای عملیات غیر بحرانی مفید است.
سؤالات متداول
میتوان، اما بیمعنی است. async بدون await() همروال را راهاندازی میکند اما نتیجه آن از دست میرود. اگر نتیجه نیاز نیست، از launch استفاده کنید — Deferred ایجاد نمیکند و از نظر حافظه و عملکرد کارآمدتر است.
استثنای داخل async در Deferred ذخیره میشود. فراخوانی await() این استثنا را منتقل میکند. برای بررسی بدون انتقال از getCompletionExceptionOrNull() استفاده کنید. هنگام استفاده از coroutineScope، خطا تمام همروالهای فرزند را لغو میکند.
awaitAll() — یک تابع توسعهدهنده برای Iterable<Deferred<T>> که همروال را تا اتمام تمام Deferredها متوقف میکند. لیست نتایج را برمیگرداند. اگر حداقل یک Deferred با خطا به پایان برسد، استثنا پرتاب میکند و بقیه را لغو میکند.
بله. async یک تابع توسعهدهنده CoroutineScope است که به scope متصل است. GlobalScope.async یک همروال بدون اتصال به چرخه حیات ایجاد میکند — میتواند بینهایت اجرا شود. در اندروید GlobalScope به دلیل خطر نشت حافظه توصیه نمیشود.
تعداد توسط توزیعکننده تعیین میشود: Dispatchers.IO تا 64 رشته همزمان را پشتیبانی میکند، Dispatchers.Default — بر اساس تعداد هستههای CPU. برای عملیات انبوه از موازیسازی محدود از طریق Semaphore یا mapNotNull با awaitAll استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید