Ένα από τα κλειδιά για τη σύνταξη μιας επιτυχημένης εφαρμογής ιστού είναι η δυνατότητα να πραγματοποιείτε δεκάδες κλήσεις AJAX ανά σελίδα.
Πρόκειται για μια τυπική πρόκληση ασύγχρονου προγραμματισμού και ο τρόπος με τον οποίο επιλέγετε να ασχολείστε με ασύγχρονες κλήσεις θα κάνει, σε μεγάλο βαθμό, ή θα διακόψει την εφαρμογή σας, και κατ 'επέκταση ενδεχομένως ολόκληρη την εκκίνηση σας.
Ο συγχρονισμός ασύγχρονων εργασιών σε JavaScript ήταν ένα σοβαρό ζήτημα για πολύ καιρό.
Αυτή η πρόκληση επηρεάζει το back-end προγραμματιστές που χρησιμοποιούν το Node.js όσο και οι προγραμματιστές front-end που χρησιμοποιούν οποιοδήποτε πλαίσιο JavaScript. Ο ασύγχρονος προγραμματισμός είναι μέρος της καθημερινής μας δουλειάς, αλλά η πρόκληση συχνά θεωρείται ελαφριά και δεν λαμβάνεται υπόψη την κατάλληλη στιγμή.
Η πρώτη και η πιο απλή λύση ήρθε με τη μορφή ένθετες λειτουργίες ως επιστροφές κλήσεων . Αυτή η λύση οδήγησε σε κάτι που ονομάζεται κόλαση και πάρα πολλές εφαρμογές εξακολουθούν να αισθάνονται το έγκαυμα του.
Τότε, έχουμε Υποσχέσεις . Αυτό το μοτίβο έκανε τον κώδικα πολύ πιο εύκολο να διαβαστεί, αλλά ήταν πολύ μακριά από την αρχή «Μην επαναλάβετε τον εαυτό σας» (DRY). Υπήρχαν πάρα πολλές περιπτώσεις όπου έπρεπε να επαναλάβετε τα ίδια κομμάτια κώδικα για να διαχειριστείτε σωστά τη ροή της εφαρμογής. Η τελευταία προσθήκη, με τη μορφή δηλώσεων ασύγχρονου / αναμονής, κατέστησε τελικά τον ασύγχρονο κώδικα σε JavaScript τόσο εύκολο να διαβαστεί όσο και να γράψει όσο οποιοδήποτε άλλο κομμάτι κώδικα.
Ας ρίξουμε μια ματιά στα παραδείγματα καθεμιάς από αυτές τις λύσεις και αναλογιστούμε την εξέλιξη του ασύγχρονου προγραμματισμού στο JavaScript.
Για να γίνει αυτό, θα εξετάσουμε μια απλή εργασία που εκτελεί τα ακόλουθα βήματα:
τι είναι ένας κόμβος διακομιστή
Η αρχαία λύση για το συγχρονισμό αυτών των κλήσεων ήταν μέσω ένθετων επιστροφών. Αυτή ήταν μια αξιοπρεπής προσέγγιση για απλές ασύγχρονες εργασίες JavaScript, αλλά δεν θα κλιμακώθηκε λόγω ενός ζητήματος που ονομάζεται κόλαση .
Ο κώδικας για τις τρεις απλές εργασίες θα μοιάζει με αυτό:
const verifyUser = function(username, password, callback){ dataBase.verifyUser(username, password, (error, userInfo) => { if (error) { callback(error) }else{ dataBase.getRoles(username, (error, roles) => { if (error){ callback(error) }else { dataBase.logAccess(username, (error) => { if (error){ callback(error); }else{ callback(null, userInfo, roles); } }) } }) } }) };
Κάθε συνάρτηση λαμβάνει ένα όρισμα που είναι μια άλλη συνάρτηση που καλείται με μια παράμετρο που είναι η απόκριση της προηγούμενης ενέργειας.
Πάρα πολλοί άνθρωποι θα βιώσουν το πάγωμα του εγκεφάλου μόνο διαβάζοντας την παραπάνω πρόταση. Έχοντας μια εφαρμογή με εκατοντάδες παρόμοια μπλοκ κώδικα θα προκαλέσει ακόμη περισσότερα προβλήματα στο άτομο που διατηρεί τον κώδικα, ακόμη και αν το έγραψε ο ίδιος.
Αυτό το παράδειγμα γίνεται ακόμη πιο περίπλοκο όταν συνειδητοποιήσετε ότι ένα database.getRoles
είναι μια άλλη συνάρτηση που έχει ένθετες επιστροφές.
const getRoles = function (username, callback){ database.connect((connection) => { connection.query('get roles sql', (result) => { callback(null, result); }) }); };
Εκτός από τον κωδικό που είναι δύσκολο να διατηρηθεί, η αρχή DRY δεν έχει καμία απολύτως αξία σε αυτήν την περίπτωση. Ο χειρισμός σφαλμάτων, για παράδειγμα, επαναλαμβάνεται σε κάθε λειτουργία και η κύρια επιστροφή κλήσης καλείται από κάθε ένθετη συνάρτηση.
Οι πιο περίπλοκες ασύγχρονες λειτουργίες JavaScript, όπως η εναλλαγή μέσω ασύγχρονων κλήσεων, είναι ακόμη μεγαλύτερη πρόκληση. Στην πραγματικότητα, δεν υπάρχει ασήμαντος τρόπος να γίνει αυτό με επιστροφές κλήσεων. Αυτός είναι ο λόγος που αρέσουν οι βιβλιοθήκες JavaScript Promise Bluebird και Ερ πήρα τόσο πολύ πρόσφυση. Παρέχουν έναν τρόπο εκτέλεσης κοινών λειτουργιών σε ασύγχρονα αιτήματα που η ίδια η γλώσσα δεν παρέχει ήδη.
Εκεί μπαίνουν οι εγγενείς υποσχέσεις JavaScript.
Υποσχέσεις ήταν το επόμενο λογικό βήμα για να ξεφύγουμε από την κόλαση. Αυτή η μέθοδος δεν αφαίρεσε τη χρήση των επιστροφών κλήσεων, αλλά έκανε την αλυσίδα των λειτουργιών απλή και απλοποίησε τον κώδικα , καθιστώντας πολύ πιο εύκολο να το διαβάσετε.
Με τις Υποσχέσεις, ο κώδικας στο ασύγχρονο παράδειγμα JavaScript θα μοιάζει με αυτό:
const verifyUser = function(username, password) { database.verifyUser(username, password) .then(userInfo => dataBase.getRoles(userInfo)) .then(rolesInfo => dataBase.logAccess(rolesInfo)) .then(finalResult => { //do whatever the 'callback' would do }) .catch((err) => { //do whatever the error handler needs }); };
Για να επιτευχθεί αυτό το είδος απλότητας, θα πρέπει να είναι όλες οι λειτουργίες που χρησιμοποιούνται στο παράδειγμα Υποσχεθεί . Ας ρίξουμε μια ματιά στο πώς το getRoles
η μέθοδος θα ενημερωθεί για να επιστρέψει ένα Promise
:
const getRoles = function (username){ return new Promise((resolve, reject) => { database.connect((connection) => { connection.query('get roles sql', (result) => { resolve(result); }) }); }); };
Έχουμε τροποποιήσει τη μέθοδο για να επιστρέψουμε ένα Promise
, με δύο επιστροφές κλήσεων και το Promise
εκτελεί ενέργειες από τη μέθοδο. Τώρα, resolve
και reject
Οι επιστροφές κλήσεων θα αντιστοιχιστούν σε Promise.then
και Promise.catch
μεθόδους αντίστοιχα.
Μπορεί να παρατηρήσετε ότι το getRoles
Η μέθοδος εξακολουθεί να είναι επιρρεπής στο φαινόμενο της πυραμίδας της μοίρας. Αυτό οφείλεται στον τρόπο δημιουργίας μεθόδων βάσης δεδομένων καθώς δεν επιστρέφουν Promise
. Εάν οι μέθοδοι πρόσβασης στη βάση δεδομένων μας επέστρεψαν επίσης Promise
το getRoles
Η μέθοδος θα μοιάζει με την ακόλουθη:
const getRoles = new function (userInfo) { return new Promise((resolve, reject) => { database.connect() .then((connection) => connection.query('get roles sql')) .then((result) => resolve(result)) .catch(reject) }); };
Η πυραμίδα της καταστροφής μετριάστηκε σημαντικά με την εισαγωγή των Υποσχέσεων. Ωστόσο, έπρεπε ακόμη να βασιστούμε σε επιστροφές κλήσεων που μεταβιβάζονται στο .then
και .catch
μέθοδοι ενός Promise
.
Οι υποσχέσεις άνοιξαν το δρόμο σε μια από τις πιο ωραίες βελτιώσεις στο JavaScript. ECMAScript 2017 έφερε συντακτική ζάχαρη στην κορυφή του Promises σε JavaScript με τη μορφή async
και await
δηλώσεις.
Μας επιτρέπουν να γράφουμε κώδικα βασισμένο σε Promise
σαν να ήταν σύγχρονο, αλλά χωρίς να μπλοκάρουμε το κύριο νήμα, καθώς αυτό το δείγμα κώδικα εμφανίζει:
const verifyUser = async function(username, password){ try { const userInfo = await dataBase.verifyUser(username, password); const rolesInfo = await dataBase.getRoles(userInfo); const logStatus = await dataBase.logAccess(userInfo); return userInfo; }catch (e){ //handle errors as needed } };
Αναμονή Promise
Η επίλυση επιτρέπεται μόνο εντός async
συναρτήσεις που σημαίνει ότι verifyUser
έπρεπε να οριστεί χρησιμοποιώντας async function
.
Ωστόσο, μόλις γίνει αυτή η μικρή αλλαγή, μπορείτε να await
οποιοδήποτε Promise
χωρίς πρόσθετες αλλαγές σε άλλες μεθόδους.
Οι συναρτήσεις Async είναι το επόμενο λογικό βήμα στην εξέλιξη του ασύγχρονου προγραμματισμού σε JavaScript. Θα κάνουν τον κωδικό σας πολύ πιο καθαρό και πιο εύκολο στη συντήρηση. Δήλωση μιας συνάρτησης ως async
θα διασφαλίσει ότι επιστρέφει πάντα ένα Promise
οπότε δεν χρειάζεται να ανησυχείτε για αυτό.
Γιατί πρέπει να αρχίσετε να χρησιμοποιείτε το JavaScript async
λειτουργεί σήμερα;
try
/ catch
όπως σε οποιονδήποτε άλλο σύγχρονο κώδικα..then
Ο αποκλεισμός δεν θα μετακινηθεί στο επόμενο .then
γιατί περνά μόνο από σύγχρονο κώδικα. Αλλά, μπορείτε να περάσετε από await
κλήσεις σαν να ήταν σύγχρονες κλήσεις.Οι δηλώσεις Async / αναμονή είναι συντακτική ζάχαρη που δημιουργήθηκε πάνω από το JavaScript Promises. Μας επιτρέπουν να γράφουμε κώδικα που βασίζεται στην υπόσχεση σαν να ήταν σύγχρονο, αλλά χωρίς να μπλοκάρουμε το κύριο νήμα.
Στο JavaScript, το callback hell είναι ένα anti-pattern στον κώδικα που συμβαίνει ως αποτέλεσμα της κακής δομής του ασύγχρονου κώδικα. Συνήθως παρατηρείται όταν οι προγραμματιστές προσπαθούν να επιβάλουν μια οπτική δομή από πάνω προς τα κάτω στον ασύγχρονο κώδικα JavaScript που βασίζεται σε επαναφορά.
Μια υπόσχεση στο JavaScript είναι σαν μια τιμή κράτησης θέσης που αναμένεται να επιλυθεί τελικά στην τελική τιμή επιτυχούς αποτελέσματος ή τον λόγο αποτυχίας.