async/await
H σύνταξη async/await χρησιμοποιείται για να απλοποιήσει τη διαχείριση ασύγχρονων λειτουργιών που βασίζονται σε Promises. Επιτρέπει τη συγγραφή ασύγχρονου κώδικα με τρόπο που μοιάζει με συγχρονική εκτέλεση, καθιστώντας τον πιο ευανάγνωστο και ευκολότερο στη συντήρηση, ιδιαίτερα σε σύνθετες ροές.
Η λέξη κλειδί async
χρησιμοποιείται μπροστά από μία συνάρτηση ή μέθοδο, καθιστώντας την ασύγχρονη. Όταν καλείται, επιστρέφει άμεσα ένα promise, το οποίο εκπληρώνεται μόλις η συνάρτηση επιστρέψει μια τιμή ή απορρίπτεται αν προκύψει κάποιο exception κατά την εκτέλεση.
Μέσα σε μια τέτοια συνάρτηση, μπορεί να χρησιμοποιηθεί η λέξη κλειδί 'await' για να «παγώσει» προσωρινά την εκτέλεση της μέχρι να ολοκληρωθεί το αντίστοιχο promise. Αν αυτό αποτύχει, ένα exception θα συμβεί στο σημείο του await, επιτρέποντας έτσι τη διαχείριση σφαλμάτων.
Ας δούμε κάποια παραδείγματα:
async function fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
const data = await response.json();
console.log(data);
}
fetchData();
// { "userId": 1,
// "id": 1,
// "title": "....",
// "body:" "..." }
Σε αυτό το παράδειγμα, η ασύγχρονη συνάρτηση fetchData
περιμένει το αποτέλεσμα της εντολής 'fetch' και το μετατρέπει σε JSON πριν το εκτυπώσει.
async function myFunction() {
try {
const result = await someAsyncFunction();
consolge.log(result);
}
catch (error) {
console.error("Σφάλμα", error.message);
}
}
Το παραπάνω παράδειγμα δείχνει τη χρήση της ασύγχρονης σύνταξης async/await σε συνδυασμό με τη δομή try/catch για διαχείριση σφαλμάτων. Η myFunction είναι μια ασύγχρονη συνάρτηση που περιμέν ει το αποτέλεσμα της someAsyncFunction() χρησιμοποιώντας το await. Αν το Promise επιλυθεί επιτυχώς, το αποτέλεσμα αναγράφεται στην κονσόλα. Αν προκύψει σφάλμα κατά την εκτέλεση της ασύγχρονης λειτουργίας, το exception εντοπίζεται στο catch και εμφανίζεται αντίστοιχο μήνυμα σφάλματος. Αυτή η προσέγγιση κάνει τη ροή του κώδικα πιο καθαρή και ευανάγνωστη, σε σύγκριση με τη χρήση 'then()' και 'catch()'.
Χειρισμός Σφαλμάτων με try/catch
Ο χειρισμός σφαλμάτων σε async
συναρτήσεις γίνεται μεσω της χρήσης της δομής try/catch
, όπως είδαμε στο προηγούμενο παράδειγμα. Αυτό μας δίνει τον έλεγχο σε κάθε πιθανό σφάλμα που μπορεί να προκύψει κατά την αναμονή ενός Promise.
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Σφάλμα κατά τη λήψη δεδομένων:", error);
}
}
H try
"περιμένει" το αποτέλεσμα της fetch
και η catch
πιάνει οποιοδήποτε σφάλμα, είτε από το δίκτυο είτε κατά την επεξεργασία των δεδομένων.
Συχνές Χρήσεις
Η σύνταξη async/await χρησιμοποιείται κυρίως σε:
- Αιτήματα σε APIs
sync function fetchUser() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
const user = await response.json();
console.log('Χρήστης:', user.name);
} catch (error) {
console.error('Σφάλμα στο fetch:', error.message);
}
}
fetchUser();
Χρησιμοποιούμε το λεκτικό await
για να περιμένουμε την απάντηση ενός API και στη συνέχεια να μετατρέψουμε τα δεδομένα σε JSON.
- Ανάγνωση αρχείων σε Node.js
const fs = require('fs/promise');
async function readFileContent() {
try {
const content = await fs.readFile('example.txt', 'utf8');
console.log('Περιεχόμενο:', content);
} catch(error) {
console.error("Σφάλμα ανάγνωσης αρχείου:", error);
}
}
readFileContent();
Διαβάζουμε ένα αρχείο τοπικά μέσω fs.promises.readFile
, χωρίς .then()
ή εμφώλευση.
- Διαδοχικές ασύγχρονες λειτουργίες με εξαρτήσεις
async function loadUserData() {
try {
const user = await fetchUser(); // Βήμα 1
const posts = await fetchPosts(user.id); // Βήμα 2
const comments = await fetchComments(posts[0].id); // Βήμα 3
console.log("Σχόλια στην πρώτη ανάρτηση:", comments);
} catch (error) {
console.error("Σφάλμα κατά τη φόρτωση δεδομένων:", error.message)
}
}
// Dummy async functions
async function fetchUser() {
return { id: 1, name: 'Νίκος' };
}
async function fetchPosts(userId) {
return [{ id: 101, title: 'Πρώτη Ανάρτηση' }];
}
async function fetchComments(postId) {
return ['Πολύ καλό άρθρο!', 'Ενδιαφέρον.'];
}
loadUserData();
Το παραπάνω παράδειγμα δείχνει πώς μπορούμε να εκτελέσουμε διαδοχικές ασύγχρονες λειτουργίες χρησιμοποιώντας τη σύνταξη async/await. Αρχικά γίνεται ανάκτηση ενός χρήστη, στη συνέχεια φορτώνονται οι αναρτήσεις του και τέλος τα σχόλια της πρώτης ανάρτησης. Όλα αυτά περικλείονται σε δομή try/catch, ώστε να εντοπίζονται και να διαχειρίζονται τυχόν σφάλματα κατά τη διάρκεια της διαδικασίας. Κάθε βήμα εξαρτάται από το προηγούμενο και χωρίς την σύνταξη async/await αυτό θα γινόταν πολύπλοκο με εμφωλευμένα .then()
.
- Αποφυγή
.then()
chaining και εμφωλευμένων callbacks
async function loadProfile() {
try {
const user = await getUser();
const settings = await getSettings(user.id);
console.log('Ρυθμίσεις για:', user.name, settings);
} catch (error) {
console.error('Σφάλμα:', error.message);
}
}
// Mock async functions
const getUser = async () => ({ id: 42, name: 'Μαρία' });
const getSettings = async (userId) => ({ theme: 'dark', language: 'el' });
loadProfile();
Αντί για getUser().then().then()...,
χρησιμοποιούμε await για να γράψουμε τον κώδικα σε "γραμμική" μορφή.
Πλεονεκτήματα
- Βελτιωμένη Αναγνωσιμότητα: Ο κώδικας είναι πιο ευανάγνωστος και μοιάζει συγχρονικός.
- Εύκολος Χειρισμός Σφαλμάτων: Η χρήση
try/catch
κάνει τον χειρισμό σφαλμάτων πολύ πιο απλό. - Αποφυγή Callback Hell: Δεν χρειάζονται εμφωλευμένα callbacks ούτε μακροσκελείς αλυσίδες
.then()
. - Καλύτερο Debugging: Ο εντοπισμός σφαλμάτων είναι πιο απλός, καθώς η ροή θυμίζει συγχρονικό κώδικα.
Σύγκριση με άλλες προσεγγίσεις
Προσέγγιση | Πλεονεκτήματα | Μειονεκτήματα |
---|---|---|
Callbacks | Απλή υλοποίηση, παλιότερη υποστήριξη | Callback hell, δύσκολος έλεγχος σφαλμάτων |
Promises | Αλυσιδωτές λειτουργίες, καλύτερη από τα callbacks | Ακόμη δύσκολος χειρισμός σε πολύπλοκες ροές |
Async/Await | Καθαρός και ευθύγραμμος κώδικας, χρήση try/catch | Απαιτεί κατανόηση των Promises και ES2017+ περιβάλλον |