JavaScript è un linguaggio di programmazione asincrono (non bloccante) e a thread singolo, il che significa che è possibile eseguire un solo processo alla volta.
Nei linguaggi di programmazione, il callback hell si riferisce generalmente a un modo inefficace di scrivere codice con chiamate asincrone. È anche conosciuta come la Piramide della sventura.
L'inferno delle callback in JavaScript viene definito come una situazione in cui viene eseguita una quantità eccessiva di funzioni di callback annidate. Riduce la leggibilità e la manutenzione del codice. La situazione dell'inferno del callback si verifica in genere quando si hanno a che fare con operazioni di richiesta asincrone, come l'esecuzione di più richieste API o la gestione di eventi con dipendenze complesse.
prova a catturare Java
Per comprendere meglio l'inferno delle callback in JavaScript, comprendere prima le callback e i loop di eventi in JavaScript.
Callback in JavaScript
JavaScript considera tutto come un oggetto, come stringhe, array e funzioni. Quindi, il concetto di callback ci consente di passare la funzione come argomento a un'altra funzione. La funzione di callback completerà prima l'esecuzione e la funzione genitore verrà eseguita successivamente.
Le funzioni di callback vengono eseguite in modo asincrono e consentono al codice di continuare l'esecuzione senza attendere il completamento dell'attività asincrona. Quando vengono combinate più attività asincrone e ciascuna attività dipende dall'attività precedente, la struttura del codice diventa complicata.
Capiamo l'uso e l'importanza dei callback. Supponiamo ad esempio di avere una funzione che accetta tre parametri, una stringa e due numeri. Vogliamo un output basato sul testo della stringa con più condizioni.
Considera l'esempio seguente:
function expectedResult(action, x, y){ if(action === 'add'){ return x+y }else if(action === 'subtract'){ return x-y } } console.log(expectedResult('add',20,10)) console.log(expectedResult('subtract',30,10))
Produzione:
30 20
Il codice precedente funzionerà correttamente, ma dobbiamo aggiungere più attività per rendere il codice scalabile. Anche il numero di istruzioni condizionali continuerà ad aumentare, il che porterà a una struttura di codice disordinata che deve essere ottimizzata e leggibile.
Possiamo quindi riscrivere il codice in un modo migliore come segue:
function add(x,y){ return x+y } function subtract(x,y){ return x-y } function expectedResult(callBack, x, y){ return callBack(x,y) } console.log(expectedResult(add, 20, 10)) console.log(expectedResult(subtract, 30, 10))
Produzione:
30 20
Tuttavia, il risultato sarà lo stesso. Ma nell'esempio precedente, abbiamo definito il corpo della funzione separato e passato la funzione come funzione di callback alla funzione awareResult. Pertanto, se vogliamo estendere la funzionalità dei risultati attesi in modo da poter creare un altro corpo funzionante con un'operazione diversa e utilizzarlo come funzione di callback, ciò renderà più semplice la comprensione e migliorerà la leggibilità del codice.
Esistono altri esempi diversi di callback disponibili nelle funzionalità JavaScript supportate. Alcuni esempi comuni sono i listener di eventi e le funzioni di array come map, reduce, filter, ecc.
Per capirlo meglio, dovremmo comprendere il pass-by-value e il pass-by-reference di JavaScript.
JavaScript supporta due tipi di dati primitivi e non primitivi. I tipi di dati primitivi sono indefiniti, null, string e booleani, che non possono essere modificati o possiamo dire comparativamente immutabili; i tipi di dati non primitivi sono array, funzioni e oggetti che possono essere modificati o mutabili.
Passa per riferimento passa l'indirizzo di riferimento di un'entità, come una funzione può essere presa come argomento. Pertanto, se il valore all'interno di quella funzione viene modificato, verrà modificato il valore originale, che è disponibile all'esterno della funzione.
In confronto, il concetto di passaggio per valore non modifica il suo valore originale, che è disponibile all'esterno del corpo della funzione. Invece, copierà il valore in due posizioni diverse utilizzando la loro memoria. JavaScript ha identificato tutti gli oggetti tramite il loro riferimento.
In JavaScript, addEventListener ascolta eventi come clic, mouseover e mouseout e accetta il secondo argomento come una funzione che verrà eseguita una volta attivato l'evento. Questa funzione viene utilizzata passando per concetto di riferimento e passata utilizzando senza parentesi.
Considera l'esempio seguente; in questo esempio, abbiamo passato una funzione di saluto come argomento in addEventListener come funzione di callback. Verrà richiamato quando viene attivato l'evento clic:
Prova.html:
Javascript Callback Example <h3>Javascript Callback</h3> Click Here to Console const button = document.getElementById('btn'); const greet=()=>{ console.log('Hello, How are you?') } button.addEventListener('click', greet)
Produzione:
Nell'esempio precedente, abbiamo passato una funzione di saluto come argomento in addEventListener come funzione di callback. Verrà richiamato quando viene attivato l'evento clic.
Allo stesso modo, anche il filtro è un esempio della funzione di callback. Se utilizziamo un filtro per iterare un array, sarà necessaria un'altra funzione di callback come argomento per elaborare i dati dell'array. Considera l'esempio seguente; in questo esempio utilizziamo la funzione maggiore per stampare il numero maggiore di 5 nell'array. Stiamo utilizzando la funzione isGreater come funzione di callback nel metodo filter.
const arr = [3,10,6,7] const isGreater = num => num > 5 console.log(arr.filter(isGreater))
Produzione:
[ 10, 6, 7 ]
L'esempio precedente mostra che la funzione maggiore viene utilizzata come funzione di callback nel metodo filter.
Per comprendere meglio i callback e i loop di eventi in JavaScript, parliamo di JavaScript sincrono e asincrono:
JavaScript sincrono
Capiamo quali sono le caratteristiche di un linguaggio di programmazione sincrono. La programmazione sincrona ha le seguenti caratteristiche:
Blocco dell'esecuzione: Il linguaggio di programmazione sincrona supporta la tecnica di esecuzione bloccante, ovvero blocca l'esecuzione delle istruzioni successive che verranno eseguite. In questo modo si ottiene l'esecuzione prevedibile e deterministica delle istruzioni.
Flusso sequenziale: La programmazione sincrona supporta il flusso di esecuzione sequenziale, il che significa che ogni istruzione viene eseguita in modo sequenziale come una dopo l'altra. Il programma linguistico attende il completamento di un'istruzione prima di passare a quella successiva.
Semplicità: Spesso la programmazione sincrona è considerata di facile comprensione perché possiamo prevederne l'ordine nel flusso di esecuzione. In generale, è lineare e facile da prevedere. È opportuno sviluppare piccole applicazioni su questi linguaggi perché possono gestire l'ordine critico delle operazioni.
cos'è un hashset in Java
Gestione diretta degli errori: In un linguaggio di programmazione sincrono la gestione degli errori è molto semplice. Se si verifica un errore durante l'esecuzione di un'istruzione, verrà generato un errore e il programma potrà rilevarlo.
In poche parole, la programmazione sincrona ha due caratteristiche principali, ovvero viene eseguita una singola attività alla volta e la serie successiva di attività successive verrà affrontata solo una volta completata l'attività corrente. Pertanto segue un'esecuzione sequenziale del codice.
Questo comportamento della programmazione durante l'esecuzione di un'istruzione, crea una situazione di codice a blocchi poiché ogni lavoro deve attendere il completamento del lavoro precedente.
Ma quando si parla di JavaScript, la risposta è sempre stata sconcertante se sia sincrono o asincrono.
Negli esempi sopra discussi, quando utilizzavamo una funzione come callback nella funzione filtro, veniva eseguita in modo sincrono. Quindi si parla di esecuzione sincrona. La funzione di filtro deve attendere che la funzione maggiore termini la sua esecuzione.
Pertanto, la funzione di callback è anche chiamata callback di blocco, poiché blocca l'esecuzione della funzione genitore in cui è stata invocata.
Principalmente, JavaScript è considerato sincrono a thread singolo e di natura bloccante. Ma utilizzando alcuni approcci, possiamo farlo funzionare in modo asincrono in base a diversi scenari.
Ora, comprendiamo il JavaScript asincrono.
JavaScript asincrono
Il linguaggio di programmazione asincrono si concentra sul miglioramento delle prestazioni dell'applicazione. I callback possono essere utilizzati in tali scenari. Possiamo analizzare il comportamento asincrono di JavaScript con l'esempio seguente:
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000)
Dall'esempio precedente, la funzione setTimeout accetta come argomenti un callback e un tempo in millisecondi. La richiamata viene richiamata dopo il tempo indicato (qui 1s). In poche parole, la funzione attenderà 1s per la sua esecuzione. Ora, dai un'occhiata al codice seguente:
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000) console.log('first') console.log('Second')
Produzione:
first Second greet after 1 second
Dal codice precedente, i messaggi di registro dopo setTimeout verranno eseguiti per primi mentre il timer trascorre. Quindi, il risultato sarà un secondo e poi il messaggio di saluto dopo un intervallo di tempo di 1 secondo.
In JavaScript, setTimeout è una funzione asincrona. Ogni volta che chiamiamo la funzione setTimeout, registra una funzione di callback (greet in questo caso) da eseguire dopo il ritardo specificato. Tuttavia, non blocca l'esecuzione del codice successivo.
Nell'esempio precedente, i messaggi di log sono le istruzioni sincrone che vengono eseguite immediatamente. Non dipendono dalla funzione setTimeout. Pertanto, eseguono e registrano i rispettivi messaggi sulla console senza attendere il ritardo specificato in setTimeout.
Nel frattempo, il ciclo di eventi in JavaScript gestisce le attività asincrone. In questo caso, attende che trascorra il ritardo specificato (1 secondo) e, trascorso tale tempo, preleva la funzione di callback (greet) e la esegue.
755 chmod
Pertanto, l'altro codice dopo la funzione setTimeout veniva eseguito mentre era in esecuzione in background. Questo comportamento consente a JavaScript di eseguire altre attività durante l'attesa del completamento dell'operazione asincrona.
Dobbiamo comprendere lo stack di chiamate e la coda di callback per gestire gli eventi asincroni in JavaScript.
Considera l'immagine qui sotto:
Dall'immagine sopra, un tipico motore JavaScript è costituito da una memoria heap e uno stack di chiamate. Lo stack di chiamate esegue tutto il codice senza attendere quando viene inserito nello stack.
La memoria heap è responsabile dell'allocazione della memoria per oggetti e funzioni in fase di esecuzione ogni volta che sono necessari.
Ora, i nostri motori browser sono costituiti da diverse API Web come DOM, setTimeout, console, fetch, ecc. e il motore può accedere a queste API utilizzando l'oggetto finestra globale. Nel passaggio successivo, alcuni loop di eventi svolgono il ruolo di gatekeeper che seleziona le richieste di funzione all'interno della coda di callback e le inserisce nello stack. Queste funzioni, come setTimeout, richiedono un certo tempo di attesa.
Ora torniamo al nostro esempio, la funzione setTimeout; quando viene incontrata la funzione, il timer viene registrato nella coda di richiamata. Successivamente, il resto del codice viene inserito nello stack di chiamate e viene eseguito una volta che la funzione raggiunge il limite del timer, è scaduta e la coda di callback invia la funzione di callback, che ha la logica specificata ed è registrata nella funzione di timeout . Pertanto, verrà eseguito dopo il tempo specificato.
Scenari infernali di richiamata
Ora abbiamo discusso i callback, gli argomenti sincroni, asincroni e altri argomenti rilevanti per l'inferno dei callback. Capiamo cos'è l'inferno del callback in JavaScript.
La situazione in cui sono annidate più callback è nota come l'inferno delle callback poiché la forma del suo codice assomiglia a una piramide, chiamata anche 'piramide del destino'.
L'inferno del callback rende più difficile comprendere e mantenere il codice. Possiamo vedere questa situazione principalmente mentre lavoriamo nel nodo JS. Ad esempio, considera l'esempio seguente:
getArticlesData(20, (articles) => { console.log('article lists', articles); getUserData(article.username, (name) => { console.log(name); getAddress(name, (item) => { console.log(item); //This goes on and on... } })
Nell'esempio precedente, getUserData accetta un nome utente che dipende dall'elenco degli articoli o deve essere estratto dalla risposta getArticles che si trova all'interno dell'articolo. getAddress ha anche una dipendenza simile, che dipende dalla risposta di getUserData. Questa situazione è chiamata inferno del callback.
Il funzionamento interno dell'inferno di richiamata può essere compreso con l'esempio seguente:
Comprendiamo che dobbiamo eseguire l'attività A. Per eseguire un'attività, abbiamo bisogno di alcuni dati dall'attività B. Allo stesso modo; abbiamo attività diverse che dipendono l'una dall'altra ed vengono eseguite in modo asincrono. Pertanto, crea una serie di funzioni di callback.
Comprendiamo le Promesse in JavaScript e il modo in cui creano operazioni asincrone, permettendoci di evitare di scrivere callback nidificati.
Promesse JavaScript
In JavaScript, le promesse sono state introdotte in ES6. È un oggetto con un rivestimento sintattico. A causa del suo comportamento asincrono è un modo alternativo per evitare di scrivere i callback per le operazioni asincrone. Al giorno d'oggi, le API Web come fetch() vengono implementate utilizzando il promettente, che fornisce un modo efficiente per accedere ai dati dal server. Ha inoltre migliorato la leggibilità del codice ed è un modo per evitare di scrivere callback annidati.
differenza tra due stringhe di Python
Le promesse nella vita reale esprimono fiducia tra due o più persone e la certezza che una cosa particolare accadrà sicuramente. In JavaScript, una Promise è un oggetto che garantisce alla produzione un unico valore in futuro (quando richiesto). Promise in JavaScript viene utilizzato per gestire e affrontare operazioni asincrone.
La Promessa restituisce un oggetto che garantisce e rappresenta il completamento o il fallimento delle operazioni asincrone e del relativo output. È un proxy per un valore senza conoscere l'output esatto. È utile per le azioni asincrone fornire un eventuale valore di successo o un motivo di fallimento. Pertanto, i metodi asincroni restituiscono i valori come un metodo sincrono.
Generalmente le promesse hanno i seguenti tre stati:
- Soddisfatto: lo stato soddisfatto si verifica quando un'azione applicata è stata risolta o completata con successo.
- In sospeso: lo stato In sospeso è quando la richiesta è in elaborazione e l'azione applicata non è stata né risolta né rifiutata ed è ancora nel suo stato iniziale.
- Rifiutato: lo stato rifiutato si verifica quando l'azione applicata è stata rifiutata, causando il fallimento dell'operazione desiderata. La causa del rifiuto può essere qualsiasi cosa, incluso il server inattivo.
La sintassi per le promesse:
let newPromise = new Promise(function(resolve, reject) { // asynchronous call is made //Resolve or reject the data });
Di seguito è riportato un esempio di scrittura delle promesse:
Questo è un esempio di come scrivere una promessa.
function getArticleData(id) { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Fetching data....'); resolve({ id: id, name: 'derik' }); }, 5000); }); } getArticleData('10').then(res=> console.log(res))
Nell'esempio sopra, possiamo vedere come possiamo utilizzare in modo efficiente le promesse per effettuare una richiesta dal server. Possiamo osservare che la leggibilità del codice è aumentata nel codice precedente rispetto ai callback. Le promesse forniscono metodi come .then() e .catch(), che ci consentono di gestire lo stato dell'operazione in caso di successo o fallimento. Possiamo specificare i casi per i diversi stati delle promesse.
Asincrono/Aspetta in JavaScript
È un altro modo per evitare l'uso di callback annidati. Async/Await ci consente di utilizzare le promesse in modo molto più efficiente. Possiamo evitare di utilizzare il concatenamento dei metodi .then() o .catch(). Questi metodi dipendono anche dalle funzioni di callback.
Async/Await può essere utilizzato con precisione con Promise per migliorare le prestazioni dell'applicazione. Ha risolto internamente le promesse e ha fornito il risultato. Inoltre, ancora una volta, è più leggibile dei metodi () o catch().
Non possiamo utilizzare Async/Await con le normali funzioni di callback. Per usarlo, dobbiamo rendere asincrona una funzione scrivendo una parola chiave async prima della parola chiave della funzione. Tuttavia, internamente utilizza anche il concatenamento.
Di seguito è riportato un esempio di Async/Await:
async function displayData() { try { const articleData = await getArticle(10); const placeData = await getPlaces(article.name); const cityData = await getCity(place) console.log(city); } catch (err) { console.log('Error: ', err.message); } } displayData();
Per utilizzare Async/Await la funzione deve essere specificata con la parola chiave async e la parola chiave wait deve essere scritta all'interno della funzione. L'asincrono interromperà la sua esecuzione finché la Promessa non verrà risolta o rifiutata. Verrà ripreso quando la Promessa sarà trattata. Una volta risolto, il valore dell'espressione wait verrà memorizzato nella variabile che la contiene.
Riepilogo:
In poche parole, possiamo evitare callback annidati utilizzando le promesse e async/await. Oltre a questi, possiamo seguire altri approcci, come scrivere commenti e dividere il codice in componenti separati può essere utile. Ma al giorno d'oggi gli sviluppatori preferiscono l'uso di async/await.
Conclusione:
L'inferno delle callback in JavaScript viene definito come una situazione in cui viene eseguita una quantità eccessiva di funzioni di callback annidate. Riduce la leggibilità e la manutenzione del codice. La situazione dell'inferno del callback si verifica in genere quando si hanno a che fare con operazioni di richiesta asincrone, come l'esecuzione di più richieste API o la gestione di eventi con dipendenze complesse.
Per comprendere meglio l'inferno del callback in JavaScript.
JavaScript considera tutto come un oggetto, come stringhe, array e funzioni. Quindi, il concetto di callback ci consente di passare la funzione come argomento a un'altra funzione. La funzione di callback completerà prima l'esecuzione e la funzione genitore verrà eseguita successivamente.
Le funzioni di callback vengono eseguite in modo asincrono e consentono al codice di continuare l'esecuzione senza attendere il completamento dell'attività asincrona.