Ricerca…


Esempi di utilizzo di callback semplici

Le callback offrono un modo per estendere la funzionalità di una funzione (o metodo) senza modificarne il codice. Questo approccio è spesso usato nei moduli (librerie / plugin), il cui codice non dovrebbe essere modificato.

Supponiamo di aver scritto la seguente funzione, calcolando la somma di una data matrice di valori:

function foo(array) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        sum += array[i];
    }
    return sum;
}

Supponiamo ora di voler fare qualcosa con ogni valore dell'array, ad esempio visualizzarlo usando alert() . Potremmo apportare le modifiche appropriate nel codice di foo , come questo:

function foo(array) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        alert(array[i]);
        sum += array[i];
    }
    return sum;
}

Ma cosa succede se decidiamo di utilizzare console.log anziché alert() ? Ovviamente cambiare il codice di foo , ogni volta che decidiamo di fare qualcos'altro con ogni valore, non è una buona idea. È molto meglio avere l'opzione di cambiare idea senza cambiare il codice di foo . Questo è esattamente il caso d'uso per i callback. Dobbiamo solo cambiare leggermente la firma e il corpo di foo :

function foo(array, callback) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        callback(array[i]);
        sum += array[i];
    }
    return sum;
}

E ora siamo in grado di cambiare il comportamento di foo cambiando semplicemente i suoi parametri:

var array = [];
foo(array, alert);
foo(array, function (x) {
    console.log(x);
});

Esempi con funzioni asincrone

In jQuery, il metodo $.getJSON() per recuperare i dati JSON è asincrono. Pertanto, il codice di passaggio in una richiamata garantisce che il codice venga chiamato dopo che è stato recuperato il JSON.

$.getJSON() :

$.getJSON( url, dataObject, successCallback );

Esempio di codice $.getJSON() :

$.getJSON("foo.json", {}, function(data) {
    // data handling code
});

Quanto segue non funzionerebbe, perché il codice per la gestione dei dati verrebbe probabilmente chiamato prima che i dati vengano effettivamente ricevuti, poiché la funzione $.getJSON richiede un intervallo di tempo non specificato e non regge lo stack di chiamate mentre attende il JSON.

$.getJSON("foo.json", {});
// data handling code

Un altro esempio di una funzione asincrona è la funzione animate() di jQuery. Poiché richiede un tempo specifico per eseguire l'animazione, a volte è preferibile eseguire del codice direttamente dopo l'animazione.

sintassi .animate() :

jQueryElement.animate( properties, duration, callback );

Ad esempio, per creare un'animazione in dissolvenza dopo la quale l'elemento scompare completamente, è possibile eseguire il codice seguente. Notare l'uso del callback.

elem.animate( { opacity: 0 }, 5000, function() {
    elem.hide();
} );

Ciò consente di nascondere l'elemento subito dopo che la funzione ha terminato l'esecuzione. Questo differisce da:

elem.animate( { opacity: 0 }, 5000 );
elem.hide();

perché quest'ultimo non aspetta che animate() (una funzione asincrona) completi, e quindi l'elemento è nascosto subito, producendo un effetto indesiderato.

Cos'è una richiamata?

Questa è una normale chiamata di funzione:

console.log("Hello World!");

Quando si chiama una funzione normale, fa il suo lavoro e quindi restituisce il controllo al chiamante.

Tuttavia, a volte una funzione deve restituire il controllo al chiamante per eseguire il proprio lavoro:

[1,2,3].map(function double(x) {
    return 2 * x;
});

Nell'esempio sopra, la funzione double è una callback per la map funzioni perché:

  1. La funzione double viene assegnata alla map funzioni dal chiamante.
  2. La map funzioni deve chiamare la funzione double zero o più volte per fare il suo lavoro.

Pertanto, la map funzioni restituisce essenzialmente il controllo al chiamante ogni volta che chiama la funzione double . Da qui il nome "callback".


Le funzioni possono accettare più di una richiamata:

promise.then(function onFulfilled(value) {
    console.log("Fulfilled with value " + value);
}, function onRejected(reason) {
    console.log("Rejected with reason " + reason);
});

Qui la funzione accetta then due funzioni di callback, onFulfilled e onRejected . Inoltre, solo una di queste due funzioni di callback viene effettivamente chiamata.

La cosa più interessante è che la funzione then ritorna prima che uno dei callback sono chiamati. Quindi, una funzione di callback può essere chiamata anche dopo che la funzione originale è ritornata.

Continuazione (sincrona e asincrona)

Le callback possono essere utilizzate per fornire il codice da eseguire dopo il completamento di un metodo:

/**
 * @arg {Function} then continuation callback
 */
function doSomething(then) {
  console.log('Doing something');
  then();
}

// Do something, then execute callback to log 'done'
doSomething(function () {
  console.log('Done');
});

console.log('Doing something else');

// Outputs:
//   "Doing something"
//   "Done"
//   "Doing something else"

Il metodo doSomething() sopra viene eseguito in modo sincrono con il callback - blocchi di esecuzione fino a quando doSomething() restituisce, assicurandosi che il callback sia eseguito prima che l'interprete si muova.

Le callback possono anche essere utilizzate per eseguire il codice in modo asincrono:

doSomethingAsync(then) {
  setTimeout(then, 1000);
  console.log('Doing something asynchronously');
}

doSomethingAsync(function() {
  console.log('Done');
});

console.log('Doing something else');

// Outputs:
//   "Doing something asynchronously"
//   "Doing something else"
//   "Done"

Le callback then sono considerate continuazioni dei metodi doSomething() . Fornire un callback come ultima istruzione in una funzione è chiamato tail-call , che è ottimizzato dagli interpreti ES2015 .

Gestione degli errori e ramificazione del flusso di controllo

Le callback sono spesso utilizzate per fornire la gestione degli errori. Questa è una forma di branching del flusso di controllo, in cui alcune istruzioni vengono eseguite solo quando si verifica un errore:

const expected = true;

function compare(actual, success, failure) {
  if (actual === expected) {
    success();
  } else {
    failure();
  }
}

function onSuccess() {
  console.log('Value was expected');
}

function onFailure() {
  console.log('Value was unexpected/exceptional');
}

compare(true, onSuccess, onFailure);
compare(false, onSuccess, onFailure);

// Outputs:
//   "Value was expected"
//   "Value was unexpected/exceptional"

L'esecuzione di codice in compare() sopra ha due rami possibili: success quando i valori attesi e reali sono gli stessi, e error quando sono diversi. Ciò è particolarmente utile quando il flusso di controllo dovrebbe ramificarsi dopo alcune istruzioni asincrone:

function compareAsync(actual, success, failure) {
  setTimeout(function () {
    compare(actual, success, failure)
  }, 1000);
}

compareAsync(true, onSuccess, onFailure);
compareAsync(false, onSuccess, onFailure);
console.log('Doing something else');

// Outputs:
//   "Doing something else"
//   "Value was expected"
//   "Value was unexpected/exceptional"

Va notato, più callback non devono essere mutuamente esclusivi - entrambi i metodi possono essere chiamati. Allo stesso modo, il compare() può essere scritto con callback che sono opzionali (usando un noop come valore predefinito - vedi schema Oggetto Nullo ).

Callback e `questo`

Spesso quando si utilizza un callback si desidera accedere a un contesto specifico.

function SomeClass(msg, elem) {
  this.msg = msg;
  elem.addEventListener('click', function() {
    console.log(this.msg);  // <= will fail because "this" is undefined
  });
}

var s = new SomeClass("hello", someElement);

soluzioni

  • Usa il bind

    bind genera in modo efficace una nuova funzione che imposta this a ciò che è stato passato al bind quindi chiama la funzione originale.

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', function() {
          console.log(this.msg);  
        }.bind(this));  // <=-  bind the function to `this`
      }
    
  • Usa le funzioni freccia

    Le funzioni di freccia rilegano automaticamente l'attuale this contesto.

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click',() => {   // <=-  arrow function binds `this`
          console.log(this.msg);  
        });
      }
    

Spesso si desidera chiamare una funzione membro, passando in modo ideale tutti gli argomenti passati all'evento sulla funzione.

soluzioni:

  • Usa il bind

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', this.handleClick.bind(this));
      }
    
      SomeClass.prototype.handleClick = function(event) {
        console.log(event.type, this.msg);
      };
    
  • Usa le funzioni freccia e l'operatore resto

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', (...a) => this.handleClick(...a));
      }
    
      SomeClass.prototype.handleClick = function(event) {
        console.log(event.type, this.msg);
      };
    
  • In particolare per i listener di eventi DOM è possibile implementare l' interfaccia EventListener

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', this);
      }
    
      SomeClass.prototype.handleEvent = function(event) {
        var fn = this[event.type];
        if (fn) {
           fn.apply(this, arguments);
        }
      };
      
      SomeClass.prototype.click = function(event) {
        console.log(this.msg);
      };
    

Richiamata usando la funzione Freccia

L'uso della funzione freccia come funzione di callback può ridurre le linee di codice.

La sintassi predefinita per la funzione freccia è

() => {}

Questo può essere usato come callback

Ad esempio se vogliamo stampare tutti gli elementi in un array [1,2,3,4,5]

senza la funzione freccia, il codice sarà simile a questo

[1,2,3,4,5].forEach(function(x){
                 console.log(x);
            }

Con la funzione freccia, può essere ridotto a

[1,2,3,4,5].forEach(x => console.log(x));

Qui la funzione di function(x){console.log(x)} callback function(x){console.log(x)} è ridotta a x=>console.log(x)



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