Sobes.tech
Senior

Πώς λειτουργούν μηχανισμοί async και await κάτω από την κουκούλα;

sobes.tech AI

Απάντηση από AI

Το Async/await στη Swift βασίζεται στη δομημένη σύγχρονη εκτέλεση και χρησιμοποιεί Dispatch/Global Actors. Οι μηχανισμοί που λειτουργούν κάτω από την κουκούλα είναι:

  1. Στυλ Συνέχειας: Οι ασύγχρονες συναρτήσεις μεταγλωττίζονται σε στυλ συνεχίσεων. Αντί για άμεση επιστροφή τιμής, ο έλεγχος περνάει στο runtime της Swift, το οποίο αποθηκεύει την τρέχουσα κατάσταση της συνάρτησης (τοπικές μεταβλητές, δείκτη εκτέλεσης). Όταν η ασύγχρονη λειτουργία ολοκληρωθεί, το runtime επαναφέρει την εκτέλεση της συνάρτησης από την αποθηκευμένη κατάσταση.

  2. Task: Κάθε κλήση μιας async συνάρτησης δημιουργεί ή συνδέεται με μια δομημένη Task. Η Task αντιπροσωπεύει μια μονάδα εργασίας και σχηματίζει ιεραρχία. Η γονική Task περιμένει την ολοκλήρωση των θυγατρικών Task.

  3. Job: Η Task διασπάται σε μικρότερες μονάδες εργασίας, που ονομάζονται Job. Το Job αντιπροσωπεύει ένα τμήμα κώδικα που μπορεί να εκτελεστεί σε έναν εκτελεστή (Executor).

  4. Executor: Ο εκτελεστής είναι υπεύθυνος για την εκκίνηση του Job. Στη βασική βιβλιοθήκη, χρησιμοποιούνται παγκόσμια DispatchQueue ως εκτελεστές για τις περισσότερες ασύγχρονες εργασίες. Τα συγκεκριμένα πλαίσια (π.χ., MainActor) έχουν τους δικούς τους εξειδικευμένους εκτελεστές.

  5. Σημεία Αναστολής: Το await είναι ένα σημείο αναστολής (suspension point). Όταν φτάσει το await, η συνάρτηση αναστέλλεται, ο έλεγχος περνάει στον καλούντα κώδικα ή στον εκτελεστή, και η τρέχουσα Job ολοκληρώνεται. Το Swift runtime αποθηκεύει την κατάσταση της συνάρτησης.

  6. Επανεκκίνηση: Όταν η ασύγχρονη λειτουργία, στην οποία περίμενε το await, ολοκληρωθεί, το Swift runtime δημιουργεί μια νέα Job για τη συνέχιση της εκτέλεσης της ανασταλμένης συνάρτησης. Αυτή η Job τοποθετείται στην ουρά του εκτελεστή.

  7. Ακύρωση: Η δομημένη σύγχρονη εκτέλεση υποστηρίζει ιεραρχική ακύρωση. Η ακύρωση μιας γονικής Task ακυρώνει αυτόματα όλες τις θυγατρικές της Task. Οι ασύγχρονες λειτουργίες μπορούν να ελέγχουν την κατάσταση ακύρωσης και να αντιδρούν ανάλογα.

Ακολουθεί ένα απλοποιημένο παράδειγμα μεταγλώττισης μιας async συνάρτησης:

// Αρχική async συνάρτηση
func fetchData() async -> Data {
    // Ασύγχρονη λειτουργία
    let data = await URLSession.shared.data(from: url).0
    return data
}

// Απλοποιημένη αναπαράσταση μεταγλώττισης
// Αντί για επιστροφή, η συνάρτηση δέχεται Continuation
func fetchData(_ continuation: Continuation<Data, Error>) {
    // Αρχικός κώδικας της συνάρτησης
    let url = URL(string: "...")! // Παράδειγμα
    
    // Εκκίνηση ασύγχρονης λειτουργίας
    URLSession.shared.data(from: url) { data, _, error in
        // Αυτό το κλείσιμο εκτελείται μετά την ολοκλήρωση της ασύγχρονης λειτουργίας
        if let data = data {
            // Μεταβίβαση αποτελέσματος στο continuation, επανεκκινώντας τη συνάρτηση
            continuation.resume(returning: data)
        } else if let error = error {
            continuation.resume(throwing: error)
        } else {
            // Διαχείριση άλλων περιπτώσεων
            continuation.resume(throwing: NSError(domain: "", code: 0, userInfo: nil))
        }
    }
    // Η συνάρτηση ολοκληρώνεται εδώ, περιμένοντας το call resume από το continuation
}

Το Await είναι ουσιαστικά μια συντακτική ζάχαρη πάνω από τη διαχείριση του Continuation και τον προγραμματισμό του Job στον εκτελεστή. Η αναστολή δεν μπλοκάρει άμεσα το νήμα, αλλά επιτρέπει στον εκτελεστή να εκτελεί άλλα Job.