Async/Await: ουσία, συναρτήσεις async και εργασία με await

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

Async/Await — είναι ένα ζεύγος λέξεων-κλειδιών για τη σύνταξη ασύγχρονου κώδικα σε σύγχρονο στυλ, διαθέσιμο σε Swift από το iOS 13 και σε Kotlin με κορουτίνες. Σύμφωνα με το Apple Swift Documentation, 2026, το Async/Await αντικαθιστά αλυσίδες callback και GCD, κάνοντας τον ασύγχρονο κώδικα γραμμικό και ευανάγνωστο. Η λέξη-κλειδί async χαρακτηρίζει μια συνάρτηση ως ασύγχρονη, ενώ το await αναστέλλει την εκτέλεσή της μέχρι τη λήψη του αποτελέσματος.

Βασικά σημεία

  • Async/Await — συντακτική ζάχαρη για ασύγχρονο κώδικα χωρίς εμφωλευμένα callbacks
  • async χαρακτηρίζει μια συνάρτηση ως ασύγχρονη και αναστέλλουσα
  • await αναστέλλει την εκτέλεση της συνάρτησης μέχρι την ολοκλήρωση της ασύγχρονης λειτουργίας
  • Task σε Swift δημιουργεί μια νέα ασύγχρονη μονάδα εργασίας με περιβάλλον
  • Structured Concurrency εγγυάται ότι όλες οι θυγατρικές εργασίες ολοκληρώνονται πριν από τη γονική

Τι είναι το Async/Await και ο ασύγχρονος προγραμματισμός

Async/Await — είναι μια γλωσσική κατασκευή που επιτρέπει τη σύνταξη ασύγχρονου κώδικα με τον ίδιο γραμμικό τρόπο όπως ο σύγχρονος. Σε Swift εμφανίστηκε στο iOS 13 / macOS 10.15 μαζί με το πλαίσιο Swift Concurrency. Σε Kotlin το async/await είναι διαθέσιμο μέσω της βιβλιοθήκης κορουτινών (kotlinx.coroutines) από την έκδοση 1.3.

Πριν από την εμφάνιση του async/await, ο ασύγχρονος κώδικας βασιζόταν σε callbacks, GCD ή RxSwift. Οι αλυσίδες εμφωλευμένων callbacks οδηγούσαν σε callback hell — βαθιά εμφώλευση που περιπλέκει την ανάγνωση και τον εντοπισμό σφαλμάτων. Το Async/Await λύνει αυτό το πρόβλημα επιτρέποντας στις ασύγχρονες συναρτήσεις να “αναστέλλονται” στα σημεία await και να συνεχίζουν μετά τη λήψη του αποτελέσματος.

Σύμφωνα με δεδομένα Google (2025), η χρήση async/await σε Kotlin μειώνει τον αριθμό γραμμών ασύγχρονου κώδικα κατά 40–60% σε σύγκριση με την προσέγγιση callback. Σε Swift, η υιοθέτηση Swift Concurrency έφτασε το 68% μεταξύ των δημοσιευμένων εφαρμογών το 2025. iOS 16 πρόσθεσε υποστήριξη Swift Concurrency σε UIKit, SwiftUI και Foundation, καθιστώντας το async/await βασικό εργαλείο για όλα τα νέα έργα Apple.

Αρχή λειτουργίας: αναστέλλουσες συναρτήσεις

Η βασική διαφορά του async/await από τα νήματα — αναστολή χωρίς αποκλεισμό. Όταν μια συνάρτηση συναντά await, αναστέλλεται, ελευθερώνοντας το τρέχον νήμα για άλλες εργασίες. Μετά την ολοκλήρωση της ασύγχρονης λειτουργίας, η συνάρτηση συνεχίζει στο ίδιο ή σε άλλο νήμα. Αυτό ονομάζεται cooperative multitasking.

Πώς λειτουργεί το Async/Await: αναστολή και επανάληψη

Η async συνάρτηση μεταγλωττίζεται σε state machine — ένα πεπερασμένο αυτόματο που διαχειρίζεται καταστάσεις εκτέλεσης. Στο σημείο await, ο μεταγλωττιστής αποθηκεύει το περιβάλλον (τοπικές μεταβλητές, διεύθυνση επιστροφής) και παραδίδει τον έλεγχο στο runtime. Όταν το αποτέλεσμα είναι έτοιμο, το runtime επαναφέρει το περιβάλλον και συνεχίζει την εκτέλεση.

Σε Swift αυτή η state machine υλοποιείται σε επίπεδο μεταγλωττιστή SIL (Swift Intermediate Language). Σε Kotlin — μέσω του μηχανισμού Continuation Passing Style (CPS): κάθε suspend συνάρτηση δέχεται μια κρυφή παράμετρο Continuation, μέσω της οποίας επιστρέφεται το αποτέλεσμα. Και οι δύο προσεγγίσεις εγγυώνται ασφαλή επανάληψη χωρίς διαρροές μνήμης.

  • Swift — ο μεταγλωττιστής μετατρέπει την async συνάρτηση σε SIL με runAsync coroutine frame
  • Kotlin — κάθε suspend συνάρτηση λαμβάνει Continuation και αναθέτει την εκτέλεση σε dispatcher
  • Και οι δύο γλώσσες — ελευθερώνουν το νήμα στο await, χωρίς να το αποκλείουν κατά την αναμονή
  • Εξαιρέσεις — διαδίδονται μέσω τυπικών μηχανισμών (throws / try-catch)

Async/Await vs Callbacks και GCD: σύγκριση προσεγγίσεων

Η προσέγγιση callback απαιτεί τη μεταβίβαση ενός closure, το οποίο καλείται μετά την ολοκλήρωση της λειτουργίας. Με τρία διαδοχικά αιτήματα προκύπτουν τρία επίπεδα εμφώλευσης με χειρισμό σφαλμάτων σε κάθε ένα. Async/Await μετατρέπει τα ίδια τρία αιτήματα σε τρεις διαδοχικές γραμμές με ενιαίο μπλοκ catch.

GCD (DispatchQueue) λύνει το πρόβλημα callback hell μέσω σειριακών ουρών και DispatchGroup, αλλά παραμένει πολυλογικό. Για μια απλή εργασία — φόρτωση user, στη συνέχεια friends, στη συνέχεια UI — απαιτείται δημιουργία ουρών, ομάδων και μπλοκ notify. Async/Await εκτελεί την ίδια εργασία με τρεις γραμμές σε μία συνάρτηση.

ΠτυχήCallbackGCDAsync/Await
ΕμφώλευσηΒαθιά (pyramid of doom)Μεσαία (ομάδες+notify)Γραμμική (flat)
Χειρισμός σφαλμάτωνΑνά callbackΧειροκίνητοςΕνιαίος (try/catch)
ΑκύρωσηΧειροκίνητηΠεριορισμένηTask.cancel()
Thread safetyΧειροκίνητηΜέσω σειριακής ουράςΜέσω MainActor

Σύμφωνα με δεδομένα Apple WWDC 2024, το Swift Concurrency async/await είναι η συνιστώμενη προσέγγιση για νέο κώδικα. Το GCD παραμένει για ενσωμάτωση με βιβλιοθήκες C και συγκεκριμένα σενάρια χαμηλού επιπέδου επεξεργασίας νημάτων.

Swift Concurrency: Task, TaskGroup και Actor

Task — είναι η βασική μονάδα ασύγχρονης εργασίας σε Swift. Το Task δημιουργείται στο περιβάλλον ενός υπάρχοντος actor ή σε οποιοδήποτε νήμα. Μέσα στο Task μπορούν να κληθούν async συναρτήσεις μέσω await. TaskGroup επιτρέπει την παράλληλη εκτέλεση πολλαπλών θυγατρικών εργασιών και τη συλλογή των αποτελεσμάτων τους — ανάλογο του DispatchGroup στα στεροειδή.

Actor — είναι ένας νηματασφαλής reference type που προστατεύει την κατάστασή του από συνθήκες ανταγωνισμού δεδομένων. Ο μεταγλωττιστής εγγυάται ότι η πρόσβαση στις ιδιότητες του actor είναι δυνατή μόνο μέσω async κλήσεων ή εντός actor isolation. Actor αντικαθιστά τη σειριακή DispatchQueue για προστασία κοινής μεταβλητής κατάστασης χωρίς χειροκίνητα κλειδώματα.

Structured Concurrency

Η αρχή Structured Concurrency ορίζει: κάθε ασύγχρονη εργασία έχει έναν γονέα και ο γονέας δεν ολοκληρώνεται έως ότου ολοκληρωθούν όλα τα παιδιά του. Αν η εργασία A εκκινήσει Task { await B() }, τότε το A αναμένει το B. Αυτό αποτρέπει διαρροές εργασιών και εξασφαλίζει προβλέψιμο κύκλο ζωής.

Async/Await σε Kotlin με κορουτίνες

Σε Kotlin το async/await υλοποιείται μέσω coroutines — ελαφρών νημάτων της γλώσσας. Η συνάρτηση χαρακτηρίζεται με τη λέξη-κλειδί suspend (ανάλογο του Swift’s async). Για εκκίνηση χρησιμοποιείται launch (fire-and-forget) ή async (με αποτέλεσμα). Το Await καλείται για τη λήψη του αποτελέσματος από async.

Dispatcher καθορίζει σε ποιο σύνολο νημάτων εκτελείται η κορουτίνα: Dispatchers.Main για UI, Dispatchers.IO για δίκτυο/δίσκο και Dispatchers.Default για εργασίες έντασης CPU. Structured Concurrency εξασφαλίζεται μέσω CoroutineScope — κατά την ακύρωση του scope ακυρώνονται όλες οι θυγατρικές κορουτίνες.

Σύμφωνα με δεδομένα JetBrains (2025), το 97% των Android εφαρμογών στο Google Play χρησιμοποιούν kotlinx.coroutines και το 82% από αυτές εφαρμόζουν async/await για δικτυακά αιτήματα. Οι coroutines έχουν γίνει de facto πρότυπο για ασύγχρονο προγραμματισμό σε Android. Kotlin Multiplatform υποστηρίζει επίσης κορουτίνες, επιτρέποντας τη χρήση κοινού ασύγχρονου κώδικα σε Android, iOS και διακομιστή.

Παραδείγματα κώδικα με Async/Await σε Swift και Kotlin

Ας δούμε δύο παραδείγματα για κάθε γλώσσα: σειριακή φόρτωση δεδομένων και παράλληλα αιτήματα με TaskGroup ή async/await.

Swift: σειριακές async κλήσεις

await αναστέλλει την εκτέλεση μέχρι τη λήψη δεδομένων χρήστη, στη συνέχεια — μέχρι τη λήψη φίλων. Καθόλου εμφωλευμένα closures.

swift
func loadProfile() async throws -> Profile {
    let user = try await api.fetchUser()
    let friends = try await api.fetchFriends(for: user.id)
    return Profile(user: user, friends: friends)
}

Swift: παράλληλα αιτήματα με TaskGroup

TaskGroup εκτελεί εργασίες παράλληλα και συλλέγει τα αποτελέσματα σε πίνακα. Η σειρά των αποτελεσμάτων μπορεί να μην συμπίπτει με τη σειρά εκκίνησης.

swift
func loadParallel() async throws -> [String] {
    await withThrowingTaskGroup(of: String.self) { group in
        group.addTask { try await api.fetchName() }
        group.addTask { try await api.fetchEmail() }
        group.addTask { try await api.fetchAvatar() }
        var results = [String]()
        for try await value in group { results.append(value) }
        return results
    }
}

Kotlin: σειριακή κλήση με async/await

Οι suspend συναρτήσεις μπορούν να καλούν άλλες suspend συναρτήσεις. Ανάλογο Swift: σειριακές κλήσεις.

kotlin
suspend fun loadProfile(): Profile {
    val user = api.fetchUser()
    val friends = api.fetchFriends(user.id)
    return Profile(user, friends)
}

Kotlin: παράλληλα αιτήματα με async/await

Σε Kotlin ο παραλληλισμός επιτυγχάνεται μέσω coroutineScope με async για κάθε αίτημα.

kotlin
suspend fun loadParallel(): List<String> = coroutineScope {
    val name = async { api.fetchName() }
    val email = async { api.fetchEmail() }
    val avatar = async { api.fetchAvatar() }
    listOf(name.await(), email.await(), avatar.await())
}

Τυπικά σφάλματα και περιορισμοί του Async/Await

Ξεχασμένο Task { } — μια ασύγχρονη συνάρτηση δεν μπορεί να κληθεί από σύγχρονο περιβάλλον χωρίς περιτύλιγμα σε Task. Σε Swift αυτό προκαλεί σφάλμα μεταγλώττισης. Σε Kotlin απαιτείται εκκίνηση μέσω lifecycleScope ή viewModelScope. Η προσπάθεια κλήσης suspend συνάρτησης από κανονική συνάρτηση δεν θα μεταγλωττιστεί.

Υπερβολική χρήση async χωρίς ανάγκη

Δεν χρειάζεται να γίνει κάθε συνάρτηση ασύγχρονη. Εργασίες έντασης CPU χωρίς I/O δεν επωφελούνται από async — είναι καλύτερο να εκτελούνται σε DispatchQueue με .userInitiated QoS. Το Async/Await είναι βελτιστοποιημένο για λειτουργίες I/O-bound: δίκτυο, δίσκος, αναμονή.

Αποκλεισμός κύριου νήματος μέσω .result

Η χρήση .result στο κύριο νήμα (σύγχρονη αναμονή αποτελέσματος async συνάρτησης) οδηγεί σε αποκλεισμό. Σε Swift το Task.synchronousWait δεν συνιστάται από την Apple. Σε Kotlin το runBlocking στο κύριο νήμα είναι αντιπρότυπο, χρησιμοποιήστε lifecycleScope.

Διαρροή Task κατά την ακύρωση

Αν το Task δεν αποθηκευτεί σε ιδιότητα κλάσης, μετά την έξοδο από το περιβάλλον ακυρώνεται. Σε Swift το Task είναι scoped — ο κύκλος ζωής του είναι συνδεδεμένος με το δημιουργό περιβάλλον. Αποθηκεύστε το Task σε ιδιότητα όταν χρειάζεστε μακροχρόνια λειτουργία.

Συχνές ερωτήσεις

Σε τι διαφέρει το async/await από το DispatchQueue;

Async/Await — γλωσσική κατασκευή που δεν απαιτεί δημιουργία ουρών. DispatchQueue — είναι ένα σύστημα API για διαχείριση νημάτων. Το async/await μεταγλωττίζεται σε state machine σε επίπεδο γλώσσας, το GCD — σε syscall σε επίπεδο OS.

Αποκλείει το await το νήμα;

Όχι — το await αναστέλλει τη συνάρτηση, αλλά ελευθερώνει το νήμα για άλλες εργασίες. Μετά την ολοκλήρωση της λειτουργίας, η συνάρτηση συνεχίζεται σε οποιοδήποτε διαθέσιμο νήμα από τη δεξαμενή.

Μπορεί να ακυρωθεί μια async εργασία;

Ναι — μέσω Task.cancel() σε Swift ή Job.cancel() σε Kotlin. Η ακύρωση διαδίδεται σε όλες τις θυγατρικές εργασίες σύμφωνα με το Structured Concurrency. Ο κώδικας ελέγχει την ένδειξη ακύρωσης μέσω Task.isCancelled ή ensureActive().

Τι είναι το MainActor σε Swift;

MainActor — είναι ένας actor του οποίου οι ιδιότητες και οι μέθοδοι εκτελούνται πάντα στο κύριο νήμα. Χαρακτηρίζοντας μια συνάρτηση με @MainActor, εγγυάστε ότι οι ενημερώσεις UI γίνονται στο σωστό νήμα, αντικαθιστώντας το DispatchQueue.main.async.

Σε ποιες γλώσσες υπάρχει async/await;

Το Async/Await είναι διαθέσιμο σε Swift (iOS 13+), Kotlin (μέσω kotlinx.coroutines), Dart (Flutter), JavaScript/TypeScript, Python, C#, Rust και Go (μέσω goroutines με συντακτική ζάχαρη).

Σύνοψη

  • Async/Await — γλωσσική κατασκευή για γραμμικό ασύγχρονο κώδικα χωρίς callback hell
  • Swift Concurrency παρέχει Task, TaskGroup, Actor και Structured Concurrency
  • Κορουτίνες Kotlin χρησιμοποιούν συναρτήσεις suspend, Dispatchers και κορουτινικά scopes
  • Αναστολή χωρίς αποκλεισμό — βασική διαφορά async/await από νήματα και ουρές
  • Structured Concurrency εγγυάται την ολοκλήρωση όλων των θυγατρικών εργασιών πριν από τη γονική
  • Async/Await vs GCD — το πρώτο προτιμάται για νέο κώδικα, το δεύτερο για legacy και χαμηλού επιπέδου
  • MainActor σε Swift και Dispatchers.Main σε Kotlin προστατεύουν το νήμα UI από λειτουργίες παρασκηνίου

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

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

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

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