Создать пользовательский обратный вызов в JavaScript

322

Все, что мне нужно сделать, это выполнить функцию обратного вызова, когда закончится выполнение моей текущей функции.

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

Потребитель для этой функции должен быть таким:

object.LoadData(success);

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

Как мне это реализовать?

Амгад Фахми
источник
3
object.LoadData(success)вызов должен быть после того, function success как определено. В противном случае вы получите сообщение о том, что функция не определена.
Дж. Бруни

Ответы:

574

На самом деле, ваш код будет работать в основном как есть, просто объявите ваш обратный вызов в качестве аргумента, и вы можете вызвать его напрямую, используя имя аргумента.

Основы

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);

Это вызов doSomething, который вызовет foo, который предупредит "все идет сюда".

Обратите внимание, что очень важно передать функцию reference ( foo), а не вызывать функцию и передавать ее результат ( foo()). В своем вопросе вы делаете это правильно, но на это просто стоит обратить внимание, потому что это распространенная ошибка.

Более продвинутые вещи

Иногда вы хотите вызвать обратный вызов, чтобы он увидел конкретное значение для this. Вы можете легко сделать это с помощью callфункции 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`

Вы также можете передать аргументы:

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`

Иногда полезно передавать аргументы, которые вы хотите передать, в виде массива, а не по отдельности. Вы можете использовать applyдля этого:

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
источник
Я знаю, что это сработает, если у меня не будет никаких параметров, подобных примеру, который вы написали, но когда я пытаюсь передать функцию с параметрами, она
выдает
@TiTaN: Странно, нет ничего особенного в передаче параметров в обратный вызов. Ссылка обратного вызова, которую вы передаете в свою функцию, является ссылкой на функцию, как и любая другая, вы можете делать с ней все обычные вещи.
TJ Crowder
4
@ Каждый, кто ответил: я думаю, что проблема TiTaN в том, что он не знает, как передать функцию, которая требует аргументов, в обратный вызов, который не передает никаких аргументов. Подумайте setTimeout(). Ответ - завернуть обратный вызов в закрытие:doSomething(function(){foo('this','should','work')})
slebetman
Кто-то указывает TiTaN на ветку (желательно на SO), обсуждающую вышеупомянутую проблему, мой поиск-фу слаб сегодня.
Slebetman
1
@Webwoman - Это зависит от вашего варианта использования. Вы можете передать его в качестве аргумента или включить его в какой-либо объект настроек / опций или любой из нескольких других опций.
TJ Crowder
78

Рекомендуется убедиться, что обратный вызов является реальной функцией, прежде чем пытаться ее выполнить:

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

  callback();
}
Дональд Нуммер младший
источник
21
if(typeof callback == "function")будет иметь тот же результат.
Reactgular
22
Да, но если обратного вызова нет, зачем его печатать? В этом суть callback && ...
theonlygusti
61

Мои 2 цента То же самое, но разные ...

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


    function dosomething(damsg, callback){
        alert(damsg);
        if(typeof callback == "function") 
        callback();
    }
</script>
К. Килиан Линдберг
источник
7
Я люблю этот фрагмент, я искал это
vimal1083
10
function loadData(callback) {

    //execute other requirement

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

loadData(function(){

   //execute callback

});
Арун Бахал
источник
6
Пожалуйста, рассмотрите возможность редактирования своего поста, чтобы добавить больше объяснения о том, что делает ваш код и почему это решит проблему. Ответ, который в основном содержит только код (даже если он работает), обычно не помогает ОП понять их проблему. Однако в этом случае это очень старый вопрос с уже опубликованными высоко оцененными ответами, поэтому, возможно, не стоит отвечать на него, когда есть новые вопросы, которые можно было бы сделать с большим вниманием.
SuperBiasedMan
1
Мне нравится этот ответ str8 вперед демонстрация того, что люди хотят видеть.
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 !");
});

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

Результат:

hello world !
Эйад Фарра
источник
4

Если вы хотите выполнить функцию, когда что-то сделано. Одним из хороших решений является прослушивание событий. Например, я буду реализовать Dispatcher, а DispatcherEventкласс с ES6, то:

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')

Диспетчер:

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)
    }))
  }
}

Удачного кодирования!

p / s: мой код отсутствует обработать некоторые исключения ошибок

Иен
источник
1
function LoadData(callback) 
{
    alert('the data have been loaded');
    callback(loadedData, currentObject);
}
Томас Бонини
источник
1

При вызове функции обратного вызова мы можем использовать ее, как показано ниже:

consumingFunction(callbackFunctionName)

Пример:

// 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

Это Codepend с полным примером.

Эрик Тан
источник
1

Некоторые из ответов, хотя и правильные, могут быть немного сложными для понимания. Вот пример с точки зрения непрофессионала:

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);

Обратный вызов означает, что «Jake» всегда добавляется пользователям перед отображением списка пользователей с помощью console.log.

Источник (YouTube)

Дэн Брей
источник
0

Пытаться:

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

Функции первого класса в JavaScript ; Вы можете просто передать их.

К Прайм
источник