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ć?

Amgad Fahmi
źródło
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`
TJ Crowder
źródło
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();
}
Donald A Nummer Jr
źródło
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>
K. Kilian Lindberg
źródło
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

});
arun bahal
źródło
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 !
Eyad Farra
źródło
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

hien
źródło
1
function LoadData(callback) 
{
    alert('the data have been loaded');
    callback(loadedData, currentObject);
}
Thomas Bonini
źródło
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.

Eric Tan
źródło
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)

Dan Bray
źródło
0

Próbować:

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

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

K Prime
źródło