Quando iniziamo a programmare per la prima volta, apprendiamo che un blocco di codice viene eseguito dall'alto verso il basso. Questa è la programmazione sincrona: ogni operazione viene completata prima che inizi il prossimo. Questo è fantastico quando stai facendo molte cose che non richiedono praticamente tempo per il completamento di un computer, come l'aggiunta di numeri, la manipolazione di una stringa o l'assegnazione di variabili.
Cosa succede quando vuoi fare qualcosa che richiede un tempo relativamente lungo, come accedere a un file su disco, inviare una richiesta di rete o aspettare che un timer scada? Nella programmazione sincrona, lo script non può fare nient'altro mentre attende.
Questo potrebbe andar bene per qualcosa di semplice o in una situazione in cui avrai più istanze dello script in esecuzione, ma per molte applicazioni server, è un incubo.
Immettere la programmazione asincrona. In uno script asincrono, il codice continua ad essere eseguito in attesa che accada qualcosa, ma può tornare indietro quando qualcosa è successo.
Prendi, ad esempio, una richiesta di rete. Se si effettua una richiesta di rete a un server lento che impiega tre secondi per rispondere, lo script può fare attivamente altre cose mentre questo server lento risponde. In questo caso, tre secondi potrebbero non sembrare molto per un essere umano, ma un server potrebbe rispondere a migliaia di altre richieste in attesa. Quindi, come gestisci l'asincronia in Node.js?
Il modo più semplice è attraverso un callback. Un callback è solo una funzione che viene chiamata quando viene completata un'operazione asincrona. Per convenzione, le funzioni di callback di Node.js hanno almeno un argomento, sbagliare
. Le callback possono avere più argomenti (che di solito rappresentano i dati restituiti al callback), ma il primo sarà sbagliare
. Come avrai intuito, sbagliare
contiene un oggetto errore (se è stato attivato un errore, più su quello successivo).
Diamo un'occhiata a un esempio molto semplice. Utilizzeremo il modulo del file system integrato di Node.js (fs
). In questo script, leggeremo il contenuto di un file di testo. L'ultima riga del file è a console.log
questo pone una domanda: se esegui questo script, pensi che vedrai il log prima di vedere il contenuto del file di testo?
var fs = require ('fs'); fs.readFile ('a-text-file.txt', // il nome del file di un file di testo che dice "Ciao!" 'utf8', // la codifica del file, in questo caso, funzione utf-8 (err , text) // callback console.log ('Errore:', err); // Errori, se qualche console.log ('Testo:', testo); // il contenuto del file); // Sarà prima o dopo l'errore / il testo? console.log ('Questo viene registrato prima o dopo il contenuto del file di testo?');
Poiché questo è asincrono, vedremo effettivamente l'ultimo console.log
prima il contenuto del file di testo. Se hai un file chiamato un-text-file.txt nella stessa directory in cui stai eseguendo il tuo script del nodo, lo vedrai sbagliare
è nullo
, e il valore di testo
è popolato con i contenuti del file di testo.
Se non hai un file chiamato un-text-file.txt, sbagliare
restituirà un oggetto Error e il valore di testo
sarà non definito
. Questo porta ad un aspetto importante dei callback: devi sempre gestire i tuoi errori. Per gestire gli errori, è necessario controllare un valore nel sbagliare
variabile; se è presente un valore, si è verificato un errore. Per convenzione, sbagliare
gli argomenti di solito non tornano falso
, quindi puoi controllare solo per sincerità.
var fs = require ('fs'); fs.readFile ('a-text-file.txt', // il nome del file di un file di testo che dice "Ciao!" 'utf8', // la codifica del file, in questo caso, funzione utf-8 (err , text) // callback if (err) console.error (err); // visualizza un errore nella console else console.log ('Testo:', testo); // nessun errore, quindi visualizza il contenuto del file);
Ora, supponiamo di voler visualizzare il contenuto di due file in un ordine particolare. Finirai con qualcosa di simile a questo:
var fs = require ('fs'); fs.readFile ('a-text-file.txt', // il nome del file di un file di testo che dice "Ciao!" 'utf8', // la codifica del file, in questo caso, funzione utf-8 (err , text) // callback if (err) console.error (err); // visualizza un errore nella console else console.log ('Primo file di testo:', testo); // nessun errore, quindi visualizza il contenuto del file fs.readFile ('another-text-file.txt', // il nome del file di un file di testo che dice "Ciao!" 'utf8', // la codifica del file, in questo caso , funzione utf-8 (err, text) // callback if (err) console.error (err); // visualizza un errore nella console else console.log ('Secondo file di testo:', testo ); // nessun errore, quindi visualizzare il contenuto del file););
Il codice sembra piuttosto sgradevole e presenta numerosi problemi:
Stai caricando i file in sequenza; sarebbe più efficiente se fosse possibile caricarli entrambi allo stesso tempo e restituire i valori quando entrambi sono stati caricati completamente.
Sintatticamente è corretto ma difficile da leggere. Si noti il numero di funzioni annidate e le schede in aumento. Potresti fare alcuni trucchi per farlo apparire un po 'meglio, ma potresti sacrificare la leggibilità in altri modi.
Non è uno scopo molto generale. Funziona bene per due file, ma cosa succede se hai a volte nove file e altre 22 o solo uno? Il modo in cui è attualmente scritto è molto rigido.
Non ti preoccupare, possiamo risolvere tutti questi problemi (e altro) con async.js.
Innanzitutto, iniziamo installando il modulo async.js.
npm install async --save
Async.js può essere utilizzato per incollare insieme matrici di funzioni in serie o in parallelo. Riscriviamo il nostro esempio:
var async = require ('async'), //async.js module fs = require ('fs'); async.series (// esegue le funzioni nel primo argomento una dopo l'altra [// Il primo argomento è una matrice di funzioni function (cb) // 'cb' è una scorciatoia per "callback" fs.readFile ('a- text-file.txt ',' utf8 ', cb);, function (cb) fs.readFile (' another-text-file.txt ',' utf8 ', cb);], funzione (err, valori ) // Il callback "done" eseguito dopo che le funzioni dell'array sono state completate se (err) // Se si sono verificati errori durante l'esecuzione delle funzioni nell'array, verranno inviati come err. Console.error ( err); else // Se err è falsy allora tutto è buono console.log ('Primo file di testo:', valori [0]); console.log ('Secondo file di testo:', valori [1]); );
Funziona quasi come nell'esempio precedente, caricando in sequenza ciascun file e differisce solo dal fatto che legge ogni file e non mostra il risultato finché non è completo. Il codice è più conciso e più pulito rispetto all'esempio precedente (e lo renderemo ancora migliore in seguito). async.series
prende una serie di funzioni e le esegue una dopo l'altra.
Ogni funzione dovrebbe avere un solo argomento, il callback (o CB
nel nostro codice). CB
dovrebbe essere eseguito con lo stesso tipo di argomenti di qualsiasi altro callback, quindi possiamo inserirlo nel nostro fs.readFile
argomenti.
Infine, i risultati vengono inviati al callback finale, il secondo argomento in async.series
. I risultati vengono memorizzati in una matrice con i valori correlati all'ordine delle funzioni nel primo argomento di async.series
.
Con async.js, la gestione degli errori è semplificata perché se incontra un errore, restituisce l'errore all'argomento del callback finale e non eseguirà ulteriori funzioni asincrone.
Una funzione correlata è async.parallel
; ha gli stessi argomenti di async.series
in modo da poter cambiare tra i due senza alterare il resto della sintassi. Questo è un buon punto per coprire il parallelo rispetto al concomitante.
JavaScript è fondamentalmente un linguaggio a thread singolo, il che significa che può fare solo una cosa alla volta. È in grado di svolgere alcune attività in un thread separato (la maggior parte delle funzioni di I / O, ad esempio), ed è qui che entra in gioco la programmazione asincrona con JS. Non confondere parallelamente concorrenza.
Quando esegui due cose con async.parallel
, non stai facendo aprire un altro thread per analizzare JavaScript o fare due cose contemporaneamente: controlli davvero quando passa tra le funzioni nel primo argomento di async.parallel
. Quindi non ottieni nulla semplicemente mettendo il codice sincrono in async.parallel.
Questo è meglio spiegato visivamente:
Ecco il nostro precedente esempio scritto per essere parallelo, l'unica differenza è che usiamo async.parallel
piuttosto che async.series
.
var async = require ('async'), //async.js module fs = require ('fs'); async.parallel (// esegue le funzioni nel primo argomento, ma non aspetti che la prima funzione termini per iniziare il secondo [// Il primo argomento è un array di funzioni function (cb) // 'cb' è una scorciatoia per "callback" fs.readFile ('a-text-file.txt', 'utf8', cb);, function (cb) fs.readFile ('another-text-file.txt', 'utf8 ', cb);], funzione (err, valori) // Il callback "done" eseguito dopo che le funzioni dell'array sono state completate if (err) // Se si sono verificati errori durante l'esecuzione delle funzioni nell'array , saranno inviati come err. console.error (err); else // Se err è falsy allora tutto è buono console.log ('Primo file di testo:', valori [0]); console.log ( 'Secondo file di testo:', valori [1]););
I nostri esempi precedenti hanno eseguito un numero fisso di operazioni, ma cosa succede se hai bisogno di un numero variabile di operazioni asincrone? Ciò diventa complicato rapidamente se ti stai semplicemente affidando a callback e costrutti di linguaggio regolari, facendo affidamento su banali contatori o controlli di condizioni che oscurano il vero significato del tuo codice. Diamo un'occhiata all'equivalente approssimativo di un ciclo for con async.js.
In questo esempio, scriveremo dieci file nella directory corrente con nomi di file sequenziali e alcuni contenuti brevi. È possibile variare il numero modificando il valore del primo argomento di async.times
. In questo esempio, il callback per fs.writeFile
crea solo un sbagliare
argomento, ma il async.times
la funzione può anche supportare un valore di ritorno. Come async.series, viene passato al callback fatto nel secondo argomento come una matrice.
var async = require ('async'), fs = require ('fs'); async.times (10, // numero di volte per eseguire la funzione function (runCount, callback) fs.writeFile ('file -' + runCount + '. txt', // il nuovo nome file 'Questo è il numero del file' + runCount, // il contenuto del nuovo file callback);, function (err) if (err) console.error (err); else console.log ('Wrote files.'););
È un buon momento per dire che la maggior parte delle funzioni async.js, per impostazione predefinita, vengono eseguite in parallelo anziché in serie. Quindi, nell'esempio sopra, inizierà a creare i file e a segnalare quando tutti sono completamente creati e scritti.
Quelle funzioni che vengono eseguite in parallelo di default hanno una funzione di serie corollaria indicata dalla funzione che termina con, avete indovinato, 'Serie'. Quindi, se volessi eseguire questo esempio in serie anziché in parallelo, cambieresti async.times
a async.timesSeries
.
Per il nostro prossimo esempio di loop, daremo un'occhiata alla funzione async.until. async.until
esegue una funzione asincrona (in serie) finché non viene soddisfatta una particolare condizione. Questa funzione accetta tre funzioni come argomenti.
La prima funzione è il test in cui si restituisce true (se si desidera interrompere il ciclo) o false (se si desidera continuare il ciclo). Il secondo argomento è la funzione asincrona e il finale è il callback fatto. Dai un'occhiata a questo esempio:
var async = require ('async'), fs = require ('fs'), startTime = new Date (). getTime (), // timestamp unix in millisecondi runCount = 0; async.until (function () // restituisce true se sono trascorsi 4 millisecondi, altrimenti false (e continua a eseguire lo script) restituisce new Date (). getTime ()> (startTime + 5);, function (callback) runCount + = 1; fs.writeFile ('timed-file -' + runCount + '. txt', // il nuovo nome file 'Questo è il numero del file' + runCount, // il contenuto del nuovo file callback);, function (err) if (err) console.error (err); else console.log ('Wrote files.'););
Questo script creerà nuovi file di testo per cinque millisecondi. All'inizio dello script, otteniamo l'ora di inizio in millisecondo di epoca unix, e quindi nella funzione di test otteniamo l'ora corrente e testiamo per vedere se è cinque millisecondi più grande dell'ora di avvio più cinque. Se esegui questo script più volte, potresti ottenere risultati diversi.
Sulla mia macchina stavo creando tra 6 e 20 file in cinque millisecondi. È interessante notare che se si tenta di aggiungere console.log
nella funzione di test o nella funzione asincrona, otterrete risultati molto diversi perché ci vuole tempo per scrivere sulla vostra console. Ciò dimostra che nel software tutto ha un costo in termini di prestazioni!
Il ciclo per ogni ciclo è una struttura a portata di mano: ti consente di fare qualcosa per ogni elemento di un array. In async.js, questo sarebbe il async.each
funzione. Questa funzione accetta tre argomenti: la raccolta o l'array, la funzione asincrona da eseguire per ciascun elemento e il callback fatto.
Nell'esempio seguente, prendiamo una serie di stringhe (in questo caso i tipi di razze di cani levrieri) e creiamo un file per ogni stringa. Quando tutti i file sono stati creati, viene eseguita la richiamata eseguita. Come ci si potrebbe aspettare, gli errori vengono gestiti tramite sbagliare
oggetto nel callback fatto. async.each
viene eseguito in parallelo, ma se si desidera eseguirlo in serie, è possibile seguire lo schema e l'utilizzo precedentemente citati async.eachSeries
invece di async.each
.
var async = require ('async'), fs = require ('fs'); async.each (// una schiera di levrieri levrieri ['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], funzione ( dogBreed, callback) fs.writeFile (dogBreed + '. txt', // il nuovo nome file 'file per cani della razza' + dogBreed, // il contenuto del nuovo file callback);, function (err) if (err) console.error (err); else console.log ('Fatto di scrivere file sui cani.'););
Un cugino di async.each
è il async.map
funzione; la differenza è che puoi riportare i valori al callback fatto. Con il async.map
funzione, si passa una matrice o una raccolta come primo argomento, quindi una funzione asincrona verrà eseguita su ciascun elemento dell'array o dell'insieme. L'ultimo argomento è il callback fatto.
L'esempio qui sotto prende l'array di razze canine e usa ogni oggetto per creare un nome di file. Il nome del file viene quindi passato a fs.readFile
, dove viene letto e i valori vengono restituiti dalla funzione di callback. Si finisce con una matrice del contenuto del file negli argomenti di callback eseguiti.
var async = require ('async'), fs = require ('fs'); async.map (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], funzione (dogBreed, callback) fs.readFile (dogBreed + '. txt', // il nuovo nome file 'utf8', callback);, function (err, dogBreedFileContents) if (err) console.error (err); else console.log ('dog razze '); console.log (dogBreedFileContents););
async.filter
è anche molto simile nella sintassi a async.each
e async.map
, ma con il filtro si invia un valore booleano alla callback dell'oggetto piuttosto che al valore del file. Nel callback fatto si ottiene un nuovo array, con solo gli elementi che hai passato a vero
o valore vero per nella callback dell'articolo.
var async = require ('async'), fs = require ('fs'); async.filter (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], funzione (dogBreed, callback) fs.readFile (dogBreed + '. txt', // il nuovo nome di file 'utf8', function (err, fileContents) if (err) callback (err); else callback (err, // questo sarà falsato da quando abbiamo controllato sopra fileContents.match (/ greyhound / gi) // usa RegExp per verificare la stringa 'greyhound' nel contenuto del file););, function (err, dogBreedFileContents) if (err) console .error (err); else console.log ('greyhound alleva:'); console.log (dogBreedFileContents););
In questo esempio, stiamo facendo alcune cose in più rispetto agli esempi precedenti. Nota come aggiungeremo una chiamata di funzione aggiuntiva e gestiremo il nostro errore. Il Se
sbagliare
e callback (err)
è molto utile se devi manipolare i risultati di una funzione asincrona, ma vuoi comunque consentire a async.js di gestire gli errori.
Inoltre, noterai che stiamo usando la variabile err come primo argomento della funzione di callback. A prima vista, questo non sembra abbastanza giusto. Ma dal momento che abbiamo già verificato la veridicità dell'errore, sappiamo che è falso e sicuro passare alla richiamata.
Finora, abbiamo esplorato una serie di elementi costitutivi utili che hanno dei corollari grezzi nella programmazione sincrona. Facciamo un tuffo async.waterfall
, che non ha molto di equivalente nel mondo sincrono.
Il concetto con una cascata è che i risultati di una funzione asincrona fluiscono negli argomenti di un'altra funzione asincrona in serie. È un concetto molto potente, soprattutto quando si tenta di mettere insieme più funzioni asincrone che si affidano l'una all'altra. Con async.waterfall
, il primo argomento è un array di funzioni e il secondo argomento è il callback fatto.
Nella serie di funzioni, la prima funzione inizia sempre con un singolo argomento, il callback. Ogni funzione successiva deve corrispondere agli argomenti non errati della funzione precedente, ad esempio la funzione err e l'aggiunta del nuovo callback.
Nel nostro prossimo esempio, inizieremo a combinare alcuni concetti usando waterfall come a colla. Nell'array che è il primo argomento, abbiamo tre funzioni: la prima carica la lista delle directory dalla directory corrente, la seconda prende la lista delle directory e usa async.map
correre fs.stat
su ogni file, e la terza funzione prende l'elenco delle directory dal risultato della prima funzione e ottiene i contenuti per ogni file (fs.readFile
).
async.waterfall
esegue ogni funzione in modo sequenziale, quindi eseguirà sempre tutto il fs.stat
funzioni prima di eseguire qualsiasi fs.readFile
. In questo primo esempio, la seconda e la terza funzione non dipendono l'una dall'altra in modo che possano essere racchiuse in un async.parallel
per ridurre il tempo di esecuzione totale, ma modificheremo nuovamente questa struttura per il prossimo esempio.
Nota: Esegui questo esempio in una piccola directory di file di testo, altrimenti avrai molta confusione a lungo nella tua finestra di terminale.
var async = require ('async'), fs = require ('fs'); async.waterfall ([function (callback) fs.readdir ('.', callback); // legge la directory corrente, passa alla funzione successiva., function (fileNames, callback) // 'fileNames' è l'elenco delle directory dalla precedente funzione async.map (fileNames, // l'elenco delle directory è solo un array di nomi di file, fs.stat, // così possiamo usare async.map per eseguire fs.stat per ogni funzione di filename (err , stats) if (err) callback (err); else callback (err, fileNames, stats); // trasmette l'errore, l'elenco delle directory e la raccolta delle statistiche all'elemento successivo nella cascata) ;, function (fileNames, stats, callback) // la directory list, 'fileNames' è unita dalla collezione di oggetti fs.stat in 'stats' async.map (fileNames, function (aFileName, readCallback) // Questa volta prendiamo i nomi dei file con la mappa e li passiamo a fs.readFile per ottenere i contenuti fs.readFile (aFileName, 'utf8', readCallback);, function (err, contents) if (err) callback (err); else // Ora la nostra richiamata w ha tre argomenti, l'elenco della directory originale ('fileNames'), la collezione fs.stats e una matrice con il contenuto di ogni callback di file (err, fileNames, stats, contents); ); ], function (err, fileNames, stats, contents) if (err) console.error (err); else console.log (fileNames); console.log (statistiche); console.log (contenuto); );
Diciamo che vogliamo ottenere i risultati solo dei file che hanno una dimensione superiore a 500 byte. Potremmo usare il codice sopra, ma otterresti la dimensione e il contenuto di ogni file, se ne avessi bisogno o meno. Come hai potuto ottenere la stat dei file e solo il contenuto dei file che raggiungono i requisiti di dimensione?
Innanzitutto, possiamo estrapolare tutte le funzioni anonime in funzioni con nome. È una preferenza personale, ma rende il codice un po 'più pulito e più facile da capire (riutilizzabile per l'avvio). Come puoi immaginare, avrai bisogno di prendere le misure, valutare quelle dimensioni e ottenere solo il contenuto dei file al di sopra delle dimensioni richieste. Questo può essere facilmente realizzato con qualcosa di simile Array.filter
, ma questa è una funzione sincrona e async.waterfall si aspetta funzioni in stile asincrono. Async.js ha una funzione di supporto che può avvolgere le funzioni sincrone in funzioni asincrone, il cui nome è piuttosto jazzistico async.asyncify
.
Dobbiamo fare tre cose, tutte con le quali ci avvolgeremo async.asyncify
. Per prima cosa prendiamo il nome del file e gli array di statistiche dal arrayFsStat
funzione, e li uniremo usando carta geografica
. Quindi filtreremo tutti gli elementi che hanno una dimensione stat inferiore a 300. Infine, prenderemo il nome file e stat combinato e useremo carta geografica
di nuovo per ottenere il nome del file.
Dopo avremo i nomi dei file con una dimensione inferiore a 300, li useremo async.map
e fs.readFile
per ottenere il contenuto. Ci sono molti modi per rompere questo uovo, ma nel nostro caso è stato suddiviso per mostrare la massima flessibilità e il riutilizzo del codice. Questo async.waterfall
uso illustra come è possibile combinare e sincronizzare il codice sincrono e asincrono.
var async = require ('async'), fs = require ('fs'); // Il nostro anonimo refactored in funzioni con funzioni function directoryListing (callback) fs.readdir ('.', Callback); function arrayFsStat (fileNames, callback) async.map (fileNames, fs.stat, function (err, stats) if (err) callback (err); else callback (err, fileNames, stats); ); function arrayFsReadFile (fileNames, callback) async.map (fileNames, function (aFileName, readCallback) fs.readFile (aFileName, 'utf8', readCallback);, function (err, contents) if (err) callback (err); else callback (err, contenuto);); // Queste funzioni sono funzione sincrona mergeFilenameAndStat (fileNames, stats) return stats.map (function (aStatObj, index) aStatObj.fileName = fileNames [index]; return aStatObj;); function above300 (combinedFilenamesAndStats) return combinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;); function justFilenames (combinedFilenamesAndStats) return combinedFilenamesAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;); async.waterfall ([directoryListing, arrayFsStat, async.asyncify (mergeFilenameAndStat), // asyncify avvolge le funzioni sincrone in una callback err-first async.asyncify (above300), async.asyncify (justFilenames), arrayFsReadFile], function (err, contenuto) if (err) console.error (err); else console.log (contenuto););
Facendo un ulteriore passo avanti, perfezioniamo ulteriormente la nostra funzione. Diciamo che vogliamo scrivere una funzione che funzioni esattamente come sopra, ma con la flessibilità di guardare in qualsiasi percorso. Un cugino vicino a async.waterfall è async.seq
. Mentre async.waterfall
esegue solo una cascata di funzioni, async.seq
restituisce una funzione che esegue una cascata di altre funzioni. Oltre a creare una funzione, puoi passare i valori che entrano nella prima funzione asincrona.
Conversione in async.seq
richiede solo alcune modifiche. Innanzitutto, modificheremo directoryListing
accettare un argomento: questo sarà il percorso. Secondo, aggiungeremo una variabile per contenere la nostra nuova funzione (directoryAbove300
). Terzo, prenderemo l'argomento dell'array dal async.waterfall
e traducilo in argomenti per async.seq
. Il nostro callback fatto per la cascata ora è usato come callback eseguito quando eseguiamo directoryAbove300
.
var async = require ('async'), fs = require ('fs'), directoryAbove300; function directoryListing (initialPath, callback) // possiamo passare una variabile nella prima funzione utilizzata in async.seq - la funzione risultante può accettare argomenti e passarli questa prima funzione fs.readdir (initialPath, callback); function arrayFsStat (fileNames, callback) async.map (fileNames, fs.stat, function (err, stats) if (err) callback (err); else callback (err, fileNames, stats); ); function arrayFsReadFile (fileNames, callback) async.map (fileNames, function (aFileName, readCallback) fs.readFile (aFileName, 'utf8', readCallback);, function (err, contents) if (err) callback (err); else callback (err, contenuto);); function mergeFilenameAndStat (fileNames, stats) return stats.map (function (aStatObj, index) aStatObj.fileName = fileNames [index]; return aStatObj;); function above300 (combinedFilenamesAndStats) return combinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;); function justFilenames (combinedFilenamesAndStats) return combinedFilenamesAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;) //async.seq produrrà una nuova funzione che è possibile utilizzare più e più volte directoryAbove300 = async.seq (directoryListing, arrayFsStat, async.asyncify (mergeFilenameAndStat), async.asyncify (above300), async.asyncify (justFilenames), arrayFsReadFile); directoryAbove300 ('.', function (err, fileNames, stats, contents) if (err) console.error (err); else console.log (fileNames););
Potresti chiederti perché non ho menzionato le promesse. Non ho nulla contro di loro - sono piuttosto pratici e forse una soluzione più elegante rispetto ai callback - ma sono un modo diverso di guardare la codifica asincrona.
Uso dei moduli Node.js integrati sbagliare
-i primi callback e migliaia di altri moduli usano questo modello. In realtà, questo è il motivo per cui questo tutorial utilizza fs
negli esempi, qualcosa di fondamentale come l'accesso al file system in Node.js utilizza i callback, quindi i codici di callback senza promesse sono una parte essenziale della programmazione Node.js.
È possibile utilizzare qualcosa come Bluebird per avvolgere callback err-first in funzioni basate su Promise, ma questo ti porta solo lontano: Async.js fornisce una serie di metafore che rendono il codice asincrono leggibile e gestibile.
JavaScript è diventato una delle lingue di fatto del lavoro sul web. Non è senza le sue curve di apprendimento, e ci sono un sacco di quadri e librerie per tenerti occupato, pure. Se stai cercando ulteriori risorse da studiare o da utilizzare nel tuo lavoro, dai un'occhiata a ciò che abbiamo a disposizione sul mercato Envato.
Ma l'apprendimento asincrono è qualcosa di completamente diverso e, si spera, questo tutorial ti abbia mostrato quanto possa essere utile.
L'asincronia è la chiave per scrivere JavaScript lato server, ma se non è realizzata correttamente, il tuo codice può diventare una bestia intrattabile di callback. Usando una libreria come async.js che fornisce una serie di metafore, potresti scoprire che scrivere codice asincrono è una gioia.