Utwórz niestandardowe wywołanie zwrotne w JavaScript


322

Wszystko, co muszę zrobić, to wykonać funkcję zwrotną, gdy zakończy się wykonywanie mojej bieżącej funkcji.

function LoadData() 
{
    alert('The data has been loaded');
    //Call my callback with parameters. For example,
    //callback(loadedData , currentObject);
}

Konsument dla tej funkcji powinien wyglądać następująco:

object.LoadData(success);

function success(loadedData , currentObject) 
{
  //Todo: some action here 
}

Jak to zaimplementować?


3
object.LoadData(success)połączenie musi być później function success zdefiniowane. W przeciwnym razie pojawi się błąd informujący, że funkcja nie jest zdefiniowana.
J. Bruni

Odpowiedzi:


574

W rzeczywistości twój kod będzie działał właściwie tak, jak jest, po prostu zadeklaruj swoje wywołanie zwrotne jako argument i możesz wywołać go bezpośrednio, używając nazwy argumentu.

Podstawy

function doSomething(callback) {
    // ...

    // Call the callback
    callback('stuff', 'goes', 'here');
}

function foo(a, b, c) {
    // I'm the callback
    alert(a + " " + b + " " + c);
}

doSomething(foo);

To zadzwoni doSomething, co zadzwoni foo, co ostrzeże „rzeczy idą tutaj”.

Zauważ, że bardzo ważne jest przekazanie funkcji referencji ( foo), zamiast wywoływania funkcji i przekazywania jej wyniku ( foo()). W swoim pytaniu robisz to poprawnie, ale warto to zaznaczyć, ponieważ jest to częsty błąd.

Bardziej zaawansowane rzeczy

Czasami chcesz zadzwonić do oddzwonienia, aby dostrzegła określoną wartość this. Możesz to łatwo zrobić za pomocą callfunkcji JavaScript :

function Thing(name) {
    this.name = name;
}
Thing.prototype.doSomething = function(callback) {
    // Call our callback, but using our own instance as the context
    callback.call(this);
}

function foo() {
    alert(this.name);
}

var t = new Thing('Joe');
t.doSomething(foo);  // Alerts "Joe" via `foo`

Możesz także przekazać argumenty:

function Thing(name) {
    this.name = name;
}
Thing.prototype.doSomething = function(callback, salutation) {
    // Call our callback, but using our own instance as the context
    callback.call(this, salutation);
}

function foo(salutation) {
    alert(salutation + " " + this.name);
}

var t = new Thing('Joe');
t.doSomething(foo, 'Hi');  // Alerts "Hi Joe" via `foo`

Czasami przydatne jest przekazanie argumentów, które mają zostać zwrócone jako tablica, a nie pojedynczo. Możesz użyć applydo tego:

function Thing(name) {
    this.name = name;
}
Thing.prototype.doSomething = function(callback) {
    // Call our callback, but using our own instance as the context
    callback.apply(this, ['Hi', 3, 2, 1]);
}

function foo(salutation, three, two, one) {
    alert(salutation + " " + this.name + " - " + three + " " + two + " " + one);
}

var t = new Thing('Joe');
t.doSomething(foo);  // Alerts "Hi Joe - 3 2 1" via `foo`

Wiem, że zadziała, jeśli nie mam żadnych parametrów, takich jak przykład, który napisałeś, ale kiedy próbuję przekazać funkcję z parametrami,
zgłasza

@TiTaN: To dziwne, nie ma nic specjalnego w przekazywaniu parametrów do wywołania zwrotnego. Odwołanie zwrotne, które przekazujesz do swojej funkcji, jest odwołaniem do funkcji, jak każda inna, możesz z nim zrobić wszystkie normalne rzeczy.
TJ Crowder

4
@ każdy, kto odpowiedział: Myślę, że problem TiTaN polega na tym, że nie wie, jak przekazać funkcję wymagającą argumentów do wywołania zwrotnego, które nie przekazuje żadnych argumentów. Myśleć setTimeout(). Odpowiedzią jest zamknięcie oddzwaniania w zamknięciu:doSomething(function(){foo('this','should','work')})
slebetman

Ktoś wskazuje TiTaN na wątek (najlepiej na SO) omawiający powyższy problem, moja wyszukiwarka jest dziś słaba.
slebetman

1
@Webwoman - To zależy od twojego przypadku użycia. Możesz przekazać go jako argument lub dołączyć do jakiegoś obiektu ustawień / opcji lub dowolnej innej opcji.
TJ Crowder

77

Dobrą praktyką jest upewnienie się, że funkcja zwrotna jest faktyczną funkcją przed próbą jej wykonania:

if (callback && typeof(callback) === "function") {

  callback();
}

21
if(typeof callback == "function")będzie miał ten sam wynik.
Reactgular

22
Tak, ale jeśli nie ma oddzwaniania, po co zawracać sobie głowę pisaniem? O to chodzi callback && ...
theonlygusti

61

Moje 2 centy Taki sam ale inny...

<script>
    dosomething("blaha", function(){
        alert("Yay just like jQuery callbacks!");
    });


    function dosomething(damsg, callback){
        alert(damsg);
        if(typeof callback == "function") 
        callback();
    }
</script>

7
Uwielbiam ten fragment, którego szukałem
vimal1083

10
function loadData(callback) {

    //execute other requirement

    if(callback && typeof callback == "function"){
        callback();
   }
}

loadData(function(){

   //execute callback

});

6
Rozważ edycję swojego posta, aby dodać więcej wyjaśnień na temat tego, co robi Twój kod i dlaczego rozwiąże problem. Odpowiedź, która w większości zawiera tylko kod (nawet jeśli działa), zwykle nie pomaga OP zrozumieć jego problemu. Jednak w tym przypadku jest to bardzo stare pytanie z dobrze ocenionymi odpowiedziami już opublikowanymi, odpowiedź na to pytanie może nie być warta, gdy pojawią się nowsze pytania, które mogłyby przykuć większą uwagę.
SuperBiasedMan,

1
Podoba mi się ta odpowiedź, która pokazuje, że ludzie chcą zobaczyć.
Aft3rL1f3

5
   function callback(e){
      return e;
   }
    var MyClass = {
       method: function(args, callback){
          console.log(args);
          if(typeof callback == "function")
          callback();
       }    
    }

==============================================

MyClass.method("hello",function(){
    console.log("world !");
});

==============================================

Wynik to:

hello world !

4

Jeśli chcesz wykonać funkcję, gdy coś jest zrobione. Jednym z dobrych rozwiązań jest słuchanie wydarzeń. Na przykład, ja zaimplementować Dispatcher, a DispatcherEventklasy z ES6, a następnie:

let Notification = new Dispatcher()
Notification.on('Load data success', loadSuccessCallback)

const loadSuccessCallback = (data) =>{
   ...
}
//trigger a event whenever you got data by
Notification.dispatch('Load data success')

Dyspozytor:

class Dispatcher{
  constructor(){
    this.events = {}
  }

  dispatch(eventName, data){
    const event = this.events[eventName]
    if(event){
      event.fire(data)
    }
  }

  //start listen event
  on(eventName, callback){
    let event = this.events[eventName]
    if(!event){
      event = new DispatcherEvent(eventName)
      this.events[eventName] = event
    }
    event.registerCallback(callback)
  }

  //stop listen event
  off(eventName, callback){
    const event = this.events[eventName]
    if(event){
      delete this.events[eventName]
    }
  }
}

DispatcherEvent:

class DispatcherEvent{
  constructor(eventName){
    this.eventName = eventName
    this.callbacks = []
  }

  registerCallback(callback){
    this.callbacks.push(callback)
  }

  fire(data){
    this.callbacks.forEach((callback=>{
      callback(data)
    }))
  }
}

Miłego kodowania!

p / s: Brakuje mojego kodu, obsłuż niektóre wyjątki błędów


1
function LoadData(callback) 
{
    alert('the data have been loaded');
    callback(loadedData, currentObject);
}

1

Podczas wywoływania funkcji oddzwonienia możemy użyć jej w następujący sposób:

consumingFunction(callbackFunctionName)

Przykład:

// Callback function only know the action,
// but don't know what's the data.
function callbackFunction(unknown) {
  console.log(unknown);
}

// This is a consuming function.
function getInfo(thenCallback) {
  // When we define the function we only know the data but not
  // the action. The action will be deferred until excecuting.
  var info = 'I know now';
  if (typeof thenCallback === 'function') {
    thenCallback(info);    
  }
}

// Start.
getInfo(callbackFunction); // I know now

To jest Codepend z pełnym przykładem.


1

Niektóre odpowiedzi, choć poprawne, mogą być nieco trudne do zrozumienia. Oto przykład w kategoriach laika:

var users = ["Sam", "Ellie", "Bernie"];

function addUser(username, callback)
{
    setTimeout(function()
    {
        users.push(username);
        callback();
    }, 200);
}

function getUsers()
{
    setTimeout(function()
    {
        console.log(users);
    }, 100);
}

addUser("Jake", getUsers);

Oddzwonienie oznacza, że ​​„Jake” jest zawsze dodawany do użytkowników przed wyświetleniem listy użytkowników console.log.

Źródło (YouTube)


0

Próbować:

function LoadData (callback)
{
    // ... Process whatever data
    callback (loadedData, currentObject);
}

Funkcje są pierwszorzędne w JavaScript ; możesz je po prostu przekazać.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.