async: τι είναι, Deferred και παράλληλες συνημμένες λειτουργίες πώς λειτουργούν

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-06-22 Χρόνος ανάγνωσης: 8 λεπ

async — Coroutine Builder στο Kotlin που εκκινεί ένα συνημμένο πρόγραμμα και επιστρέφει ένα αντικείμενο Deferred<T> για τη λήψη του αποτελέσματος μιας ασύγχρονης λειτουργίας. Το async επιτρέπει την παράλληλη εκτέλεση πολλαπλών εργασιών και τη συνάθροιση των αποτελεσμάτων τους μέσω του await(). Σύμφωνα με τον οδηγό της JetBrains, 2024, το async είναι ο προτιμώμενος τρόπος οργάνωσης παράλληλων υπολογισμών σε συνημμένες λειτουργίες.

Κύρια Σημεία

  • async — κατασκευαστής συνημμένων προγραμμάτων που επιστρέφει Deferred<T> με το αποτέλεσμα της ασύγχρονης λειτουργίας
  • Deferred — ασύγχρονη υπόσχεση, που κληρονομεί το Job, με τη μέθοδο await() για τη λήψη του αποτελέσματος
  • async επιτρέπει την παράλληλη εκτέλεση ανεξάρτητων εργασιών, μειώνοντας τον συνολικό χρόνο
  • await() — συνάρτηση suspend που αναστέλλει το συνημμένο πρόγραμμα έως ότου ληφθεί το αποτέλεσμα χωρίς αποκλεισμό
  • Δομημένη ταυτοχρονία με async εγγυάται την ακύρωση όλων των παράλληλων εργασιών σε περίπτωση σφάλματος

Τι είναι το async στο Kotlin Coroutines;

async — είναι μια συνάρτηση επέκτασης του CoroutineScope που δημιουργεί ένα συνημμένο πρόγραμμα με επιστροφή αποτελέσματος. Σε αντίθεση με το launch, το async επιστρέφει Deferred<T> — ένα αντικείμενο που αντιπροσωπεύει μια μελλοντική τιμή. Το συνημμένο πρόγραμμα που εκκινείται μέσω async εκτελείται ταυτόχρονα με άλλα συνημμένα προγράμματα, επιτρέποντας τον παραλληλισμό υπολογισμών.

Το async χρησιμοποιείται όταν το αποτέλεσμα μιας ασύγχρονης λειτουργίας χρειάζεται για περαιτέρω επεξεργασία: φόρτωση δεδομένων από πολλαπλές πηγές, επεξεργασία παρτίδας συλλογών, παράλληλα αιτήματα σε διάφορα API. Deferred δημιουργείται αμέσως και το αποτέλεσμα γίνεται διαθέσιμο μετά την ολοκλήρωση του συνημμένου προγράμματος.

kotlin
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 και Deferred

Η υπογραφή του async είναι παρόμοια με το launch: οι ίδιες παράμετροι CoroutineContext, CoroutineStart και μπλοκ suspend. Η διαφορά είναι στον τύπο επιστροφής — Deferred<T> αντί για Job, όπου T είναι ο τύπος του αποτελέσματος που επιστρέφεται από την τελευταία έκφραση του μπλοκ.

kotlin
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 εκκινεί τα συνημμένα προγράμματα ταυτόχρονα, μειώνοντας τον συνολικό χρόνο στον χρόνο της μεγαλύτερης λειτουργίας.

Παράλληλη φόρτωση δεδομένων

Τυπικό σενάριο — φόρτωση προφίλ χρήστη, ρυθμίσεων και ιστορικού παραγγελιών με τρία παράλληλα async αιτήματα. Μετά την ολοκλήρωση και των τριών, τα αποτελέσματα συνδυάζονται σε ένα ενιαίο μοντέλο δεδομένων.

kotlin
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

Το Deferred υποστηρίζει διάφορες μεθόδους για εργασία με το αποτέλεσμα. Εκτός από την await(), υπάρχουν μέθοδοι για έλεγχο κατάστασης, λήψη της έτοιμης τιμής και συνάθροιση πολλαπλών Deferred μέσω βοηθητικών συναρτήσεων του Kotlin Coroutines.

ΜέθοδοςΠεριγραφήSuspend;
await()Αναστέλλει το συνημμένο πρόγραμμα έως ότου ληφθεί το αποτέλεσμαΝαι
getCompleted()Επιστρέφει το αποτέλεσμα αν το συνημμένο πρόγραμμα ολοκληρώθηκε (αλλιώς Exception)Όχι
getCompletionExceptionOrNull()Επιστρέφει την εξαίρεση σε περίπτωση σφάλματος ή nullΌχι
isCompletedΕλέγχει αν το συνημμένο πρόγραμμα ολοκληρώθηκεΌχι

Για τη συνάθροιση μιας λίστας Deferred χρησιμοποιείται το awaitAll() — μια συνάρτηση που αναστέλλει την εκτέλεση έως ότου ολοκληρωθούν όλα τα Deferred στη συλλογή. Αν τουλάχιστον ένα ολοκληρώθηκε με σφάλμα, το awaitAll() πετάει εξαίρεση.

kotlin
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
    return coroutineScope {
        val deferreds = requests.map { async { it() } }
        deferreds.awaitAll()
    }
}

Διαφορές μεταξύ async και launch

Η επιλογή μεταξύ async και launch εξαρτάται από το αν χρειάζεται το αποτέλεσμα εκτέλεσης του συνημμένου προγράμματος. Το launch επιστρέφει Job και είναι κατάλληλο για λειτουργίες fire-and-forget, το async επιστρέφει Deferred και χρησιμοποιείται για εργασίες που επιστρέφουν δεδομένα. Από άποψη σύνταξης και παραμέτρων, οι κατασκευαστές είναι πανομοιότυποι.

  • launch → Job (χωρίς αποτέλεσμα), async → Deferred<T> (έχει αποτέλεσμα T)
  • launch — εργασίες fire-and-forget (καταγραφή, προσωρινή αποθήκευση, αποστολή αναλυτικών στοιχείων)
  • async — παράλληλοι υπολογισμοί (φόρτωση από πολλαπλά API, επεξεργασία παρτίδας)
  • Και τα δύο υποστηρίζουν δομημένη ταυτοχρονία και παραμέτρους CoroutineStart
  • Το await() είναι διαθέσιμο μόνο για Deferred, το launch δεν παρέχει αποτέλεσμα

Σημαντικός κανόνας: μην χρησιμοποιείτε ποτέ async για fire-and-forget. Αν το αποτέλεσμα του συνημμένου προγράμματος δεν χρειάζεται, χρησιμοποιήστε launch. Το async δημιουργεί επιπλέον επιβάρυνση για τη δημιουργία Deferred, που δεν δικαιολογείται σε ένα τέτοιο σενάριο.

Παραδείγματα async στην κινητή ανάπτυξη

Στο Android, το async χρησιμοποιείται για παράλληλες λειτουργίες εντός ViewModel και UseCases. Τυπικά σενάρια: ταυτόχρονη φόρτωση προφίλ και ροής ειδήσεων, λήψη καιρού από πολλές μετεωρολογικές υπηρεσίες, συγχρονισμός δεδομένων παρτίδας με τον διακομιστή.

kotlin
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;

Μπορεί, αλλά είναι άσκοπο. Το async χωρίς await() εκκινεί το συνημμένο πρόγραμμα, αλλά το αποτέλεσμά του χάνεται. Αν το αποτέλεσμα δεν χρειάζεται, χρησιμοποιήστε launch — δεν δημιουργεί Deferred και είναι πιο αποδοτικό σε μνήμη και απόδοση.

Πώς χειρίζεται το async τις εξαιρέσεις;

Η εξαίρεση μέσα στο async αποθηκεύεται στο Deferred. Η κλήση await() προωθεί αυτήν την εξαίρεση. Για έλεγχο χωρίς προώθηση, χρησιμοποιήστε getCompletionExceptionOrNull(). Κατά τη χρήση coroutineScope, το σφάλμα ακυρώνει όλα τα θυγατρικά συνημμένα προγράμματα.

Τι είναι το awaitAll στο Kotlin Coroutines;

awaitAll() — συνάρτηση επέκτασης για Iterable<Deferred<T>> που αναστέλλει το συνημμένο πρόγραμμα έως ότου ολοκληρωθούν όλα τα Deferred. Επιστρέφει λίστα αποτελεσμάτων. Αν τουλάχιστον ένα Deferred ολοκληρωθεί με σφάλμα, προωθεί την εξαίρεση και ακυρώνει τα υπόλοιπα.

Διαφέρει το async από το GlobalScope.async;

Ναι. Το async είναι συνάρτηση επέκτασης CoroutineScope, δεσμευμένη στο scope. Το GlobalScope.async δημιουργεί ένα συνημμένο πρόγραμμα χωρίς σύνδεση με τον κύκλο ζωής — μπορεί να εκτελείται επ' αόριστον. Στο Android, το GlobalScope δεν συνιστάται λόγω κινδύνου διαρροών μνήμης.

Πόσα async συνημμένα προγράμματα μπορούν να εκκινηθούν για παράλληλα αιτήματα;

Ο αριθμός υπαγορεύεται από τον διανομέα: το Dispatchers.IO υποστηρίζει έως 64 ταυτόχρονα νήματα, το Dispatchers.Default — ανάλογα με τον αριθμό πυρήνων CPU. Για μαζικές λειτουργίες, χρησιμοποιήστε περιορισμένο παραλληλισμό μέσω Semaphore ή mapNotNull με awaitAll.

Σύνοψη

  • async — κατασκευαστής συνημμένων προγραμμάτων που επιστρέφει Deferred<T> για ασύγχρονη λήψη αποτελέσματος
  • Deferred κληρονομεί το Job και προσθέτει τις μεθόδους await(), getCompleted(), getCompletionExceptionOrNull()
  • Παράλληλη εκτέλεση με async μειώνει τον συνολικό χρόνο στη διάρκεια της μεγαλύτερης λειτουργίας
  • awaitAll() — βοηθητικό εργαλείο για αναμονή όλων των Deferred σε μια συλλογή
  • coroutineScope — συνιστώμενο περιτύλιγμα για μια ομάδα async με εγγύηση ακύρωσης
  • Μην χρησιμοποιείτε async για fire-and-forget — εφαρμόστε launch
  • Στο Android, το async χρησιμοποιείται για παράλληλη φόρτωση στο ViewModel με viewModelScope

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης