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 μπορεί να χρησιμοποιηθεί σε διάφορα σενάρια, όπως:
- Ανάγνωση αρχείων σε 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 | Απλή υλοποίηση, παλιότερη υποστήριξη | Callback hell, δύσκολος έλεγχος σφαλμάτων |
Promises | Αλυσιδωτές λειτουργίες, καλύτερη από τα callbacks | Ακόμη δύσκολος χειρισμός σε πολύπλοκες ροές |
async/await | Καθαρός και γραμμικός κώδικας, χρήση try/catch | Απαιτεί κατανόηση των Promises και ES2017+ περι βάλλον |