Ricerca…


introduzione

Il nodo è un linguaggio di programmazione in cui tutto può essere eseguito in modo asincrono. Di seguito è possibile trovare alcuni esempi e le cose tipiche del funzionamento asincrono.

Sintassi

  • doSomething ([args], function ([argsCB]) {/ * fa qualcosa quando è fatto * /});
  • doSomething ([args], ([argsCB]) => {/ * fa qualcosa quando viene fatto * /});

Funzioni di callback

Funzioni di callback in JavaScript

Le funzioni di callback sono comuni in JavaScript. Le funzioni di callback sono possibili in JavaScript perché le funzioni sono cittadini di prima classe .

Callback sincroni.

Le funzioni di callback possono essere sincrone o asincrone. Poiché le funzioni di callback asincrone possono essere più complesse, ecco un semplice esempio di una funzione di callback sincrono.

// a function that uses a callback named `cb` as a parameter
function getSyncMessage(cb) {
    cb("Hello World!");
}

console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getSyncMessage(function(message) {
    console.log(message);
});
console.log("After getSyncMessage call");

L'output per il codice sopra riportato è:

> Before getSyncMessage call
> Hello World!
> After getSyncMessage call

Innanzitutto analizzeremo come viene eseguito il codice precedente. Questo è più per coloro che non comprendono già il concetto di callback se già lo capisci, sentiti libero di saltare questo paragrafo. Prima viene analizzato il codice e quindi la prima cosa interessante che si verifica è che viene eseguita la riga 6 che emette Before getSyncMessage call alla console. Quindi viene eseguita la riga 8 che chiama la funzione getSyncMessage invia in una funzione anonima come argomento per il parametro cb nella funzione getSyncMessage . L'esecuzione è ora eseguita all'interno della funzione getSyncMessage sulla riga 3 che esegue la funzione cb che è stata appena getSyncMessage , questa chiamata invia una stringa di argomento "Hello World" per il parametro denominato message nella funzione anonima passata. L'esecuzione quindi va alla riga 9 che registra Hello World! alla console. Quindi l'esecuzione passa attraverso il processo di uscita dal callstack ( vedere anche ) che colpisce la linea 10 quindi la linea 4 e infine torna alla riga 11.

Alcune informazioni da sapere sui callback in generale:

  • La funzione che invii a una funzione come richiamata può essere chiamata zero volte, una volta o più volte. Tutto dipende dall'implementazione.
  • La funzione di callback può essere chiamata in modo sincrono o asincrono e possibilmente in modo sincrono e asincrono.
  • Proprio come le normali funzioni, i nomi che dai parametri alla tua funzione non sono importanti ma l'ordine è. Così, per esempio, sulla riga 8 il message parametri potrebbe essere stato chiamato statement , msg , o se non hai senso qualcosa di simile a jellybean . Quindi dovresti sapere quali parametri sono stati inviati alla tua callback in modo da poterli ottenere nell'ordine giusto con nomi propri.

Callback asincroni.

Una cosa da notare su JavaScript è che è sincrono di default, ma ci sono delle API date nell'ambiente (browser, Node.js, ecc.) Che potrebbero renderlo asincrono (c'è di più a riguardo qui ).

Alcune cose comuni che sono asincrone negli ambienti JavaScript che accettano i callback:

  • eventi
  • setTimeout
  • setInterval
  • l'API di recupero
  • promesse

Anche qualsiasi funzione che utilizza una delle funzioni sopra può essere avvolta con una funzione che richiede una richiamata e il callback sarebbe quindi una richiamata asincrona (sebbene il wrapping di una promessa con una funzione che richiede una richiamata sia probabilmente considerato un anti-pattern come ci sono più modi preferiti per gestire le promesse).

Quindi data questa informazione possiamo costruire una funzione asincrona simile alla precedente sincrona.

// a function that uses a callback named `cb` as a parameter
function getAsyncMessage(cb) {
    setTimeout(function () { cb("Hello World!") }, 1000);
}

console.log("Before getSyncMessage call");
// calling a function and sending in a callback function as an argument.
getAsyncMessage(function(message) {
    console.log(message);
});
console.log("After getSyncMessage call");

Che stampa quanto segue alla console:

> Before getSyncMessage call
> After getSyncMessage call
// pauses for 1000 ms with no output
> Hello World!

L'esecuzione della linea passa ai registri della linea 6 "Prima della chiamata getSyncMessage". Quindi l'esecuzione passa alla riga 8 chiamando getAsyncMessage con un callback per il parametro cb . Viene quindi eseguita la riga 3 che chiama setTimeout con un callback come primo argomento e il numero 300 come secondo argomento. setTimeout fa tutto ciò che fa e trattiene quel callback in modo che possa chiamarlo più tardi in 1000 millisecondi, ma dopo aver impostato il timeout e prima che metta in pausa i 1000 millisecondi restituisce l'esecuzione al punto in cui era stata interrotta, quindi va alla riga 4 , quindi la riga 11, quindi si interrompe per 1 secondo e setTimeout chiama la sua funzione di richiamata che riprende l'esecuzione alla riga 3 dove getAsyncMessages richiamato il callback getAsyncMessages con il valore "Hello World" per il message parametro che viene quindi registrato sulla console alla riga 9 .

Funzioni di callback in Node.js

NodeJS ha callback asincroni e in genere fornisce due parametri alle tue funzioni a volte convenzionalmente chiamati err e data . Un esempio con la lettura di un file di testo.

const fs = require("fs");

fs.readFile("./test.txt", "utf8", function(err, data) {
    if(err) {
        // handle the error 
    } else {
        // process the file text given with data
    }
});

Questo è un esempio di callback che viene chiamato una volta sola.

È buona pratica gestire l'errore in qualche modo anche se lo si registra o lo si gira. Il resto non è necessario se si lancia o si torna e può essere rimosso per ridurre il rientro finché si interrompe l'esecuzione della funzione corrente nel se facendo qualcosa come il lancio o il ritorno.

Sebbene possa essere comune vedere err , i data potrebbero non essere sempre il caso in cui i callback useranno tale modello, è meglio guardare la documentazione.

Un altro esempio di callback proviene dalla libreria express (espressa 4.x):

// this code snippet was on http://expressjs.com/en/4x/api.html
const express = require('express');
const app = express();

// this app.get method takes a url route to watch for and a callback
// to call whenever that route is requested by a user.
app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);

Questo esempio mostra una richiamata che viene chiamata più volte. Il callback è fornito con due oggetti come parametri qui denominati come req e res questi nomi corrispondono rispettivamente a richiesta e risposta, e forniscono modi per visualizzare la richiesta in entrata e impostare la risposta che verrà inviata all'utente.

Come puoi vedere ci sono vari modi in cui un callback può essere usato per eseguire il codice sincrono e asincrono in JavaScript e le callback sono molto diffuse in tutto il codice JavaScript.

Esempio di codice

Domanda: Qual è l'output del codice qui sotto e perché?

setTimeout(function() {
    console.log("A");
}, 1000);

setTimeout(function() {
    console.log("B");
}, 0);

getDataFromDatabase(function(err, data) {
    console.log("C");
    setTimeout(function() {
        console.log("D");
    }, 1000);
});

console.log("E");

Uscita: questo è noto con certezza: EBAD . C è sconosciuto quando verrà registrato.

Spiegazione: Il compilatore non si fermerà sui getDataFromDatabase setTimeout e getDataFromDatabase . Quindi la prima riga che registrerà è E Le funzioni di callback (primo argomento di setTimeout ) verranno eseguite dopo il timeout impostato su un modo asincrono!

Più dettagli:

  1. E non ha setTimeout
  2. B ha un timeout impostato di 0 millisecondi
  3. A ha un timeout impostato di 1000 millisecondi
  4. D deve richiedere un database, dopo che D deve attendere 1000 millisecondi quindi viene dopo A
  5. C è sconosciuto perché è sconosciuto quando vengono richiesti i dati del database. Potrebbe essere prima o dopo A

Gestione degli errori asincroni

Prova a prendere

Gli errori devono sempre essere gestiti. Se si utilizza la programmazione sincrona, è possibile utilizzare un try catch . Ma questo non funziona se lavori asincroni! Esempio:

try {
    setTimeout(function() {
        throw new Error("I'm an uncaught error and will stop the server!");
    }, 100); 
}
catch (ex) {
    console.error("This error will not be work in an asynchronous situation: " + ex);
}

Gli errori asincroni verranno gestiti solo all'interno della funzione di callback!

Possibilità di lavoro

v0.8

Gestori di eventi

Le prime versioni di Node.JS hanno un gestore di eventi.

process.on("UncaughtException", function(err, data) { 
    if (err) {
        // error handling
    }
});
v0.8

domini

All'interno di un dominio, gli errori vengono rilasciati tramite gli emettitori di eventi. Usando questo sono tutti gli errori, i timer, i metodi di callback implicitamente registrati solo all'interno del dominio. In caso di errore, inviare un evento di errore e non arrestare l'applicazione.

var domain = require("domain");
var d1 = domain.create();
var d2 = domain.create();

d1.run(function() {
    d2.add(setTimeout(function() {
        throw new Error("error on the timer of domain 2");
    }, 0));
});

d1.on("error", function(err) {
    console.log("error at domain 1: " + err);
});

d2.on("error", function(err) {
    console.log("error at domain 2: " + err);
});

Callback hell

L'inferno del callback (anche una piramide di doom o effetto boomerang) si verifica quando si annidano troppe funzioni di callback all'interno di una funzione di callback. Ecco un esempio per leggere un file (in ES6).

const fs = require('fs');
let filename = `${__dirname}/myfile.txt`;

fs.exists(filename, exists => {
    if (exists) {
        fs.stat(filename, (err, stats) => {
            if (err) {
                throw err;
            }
            if (stats.isFile()) {
                fs.readFile(filename, null, (err, data) => {
                    if (err) {
                        throw err;
                    }
                    console.log(data);
                });
            }
            else {
                throw new Error("This location contains not a file");
            }
        });
    }
    else {
        throw new Error("404: file not found");
    }
}); 

Come evitare "Callback Hell"

Si consiglia di annidare non più di 2 funzioni di callback. Questo ti aiuterà a mantenere la leggibilità del codice e sarà molto più facile mantenerlo in futuro. Se hai bisogno di nidificare più di 2 callback, prova a utilizzare gli eventi distribuiti .

Esiste anche una libreria chiamata async che aiuta a gestire i callback e la loro esecuzione disponibile su npm. Aumenta la leggibilità del codice di callback e offre un maggiore controllo sul flusso del codice di callback, incluso il permesso di eseguirli in parallelo o in serie.

Promesse native

v6.0.0

Le promesse sono uno strumento per la programmazione asincrona. In JavaScript promesse sono noti per le loro then metodi. Le promesse prevedono due stati principali "in attesa" e "risolti". Una volta che la promessa è 'risolta', non può tornare 'in sospeso'. Ciò significa che le promesse sono valide soprattutto per eventi che si verificano solo una volta. Lo stato 'sistemato' ha due stati pure 'risolti' e 'rifiutati'. Puoi creare una nuova promessa usando la new parola chiave e passando una funzione nel costruttore new Promise(function (resolve, reject) {}) .

La funzione passata nel costruttore Promise riceve sempre un primo e un secondo parametro, solitamente denominati rispettivamente resolve e reject . La denominazione di questi due parametri è convenzione, ma metteranno la promessa nello stato 'risolto' o nello stato 'rifiutato'. Quando uno di questi è chiamato la promessa passa dall'essere "in sospeso" a "risolto". resolve viene chiamata quando l'azione desiderata, che è spesso asincrona, è stata eseguita e il reject viene utilizzato se l'azione è stata errata.

Nel sotto timeout è una funzione che restituisce una Promessa.

function timeout (ms) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve("It was resolved!");
    }, ms)
  });
}

timeout(1000).then(function (dataFromPromise) {
  // logs "It was resolved!"
  console.log(dataFromPromise);
})

console.log("waiting...");

uscita della console

waiting...
// << pauses for one second>>
It was resolved!

Quando viene chiamato timeout, la funzione passata al costruttore Promise viene eseguita senza ritardo. Quindi viene eseguito il metodo setTimeout e la richiamata viene attivata nei successivi ms millisecondi, in questo caso ms=1000 . Poiché la richiamata al setTimeout non viene attivata, la funzione di timeout restituisce il controllo all'ambito della chiamata. La catena dei metodi then viene quindi archiviata per essere chiamata più tardi quando / se la Promessa è stata risolta. Se esistessero metodi di catch , anche questi sarebbero archiviati, ma verrebbero licenziati quando / se la promessa 'respinge'.

Lo script stampa quindi "in attesa ...". Un secondo dopo, setTimeout chiama il suo callback che chiama la funzione di risoluzione con la stringa "È stato risolto!". Tale stringa viene quindi passata al callback del metodo then e quindi viene registrata all'utente.

Nello stesso senso è possibile racchiudere la funzione setTimeout asincrona che richiede una richiamata, quindi è possibile racchiudere qualsiasi azione asincrona singolare con una promessa.

Maggiori informazioni sulle promesse nella documentazione di JavaScript Promises .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow