Створіть спеціальний зворотний дзвінок у 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, що сповістить "речі йдуть сюди".

Зауважте, що дуже важливо передавати функцію посилання ( 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`

Я знаю, що це спрацює, якщо у мене немає таких параметрів, як приклад, який ви написали, але коли я намагаюся передавати функцію з параметрами, це кидає виняток і каже мені, що функція не визначена
Amgad Fahmi

@TiTaN: Дивно, що в передачі параметрів у зворотний виклик немає нічого особливого. Посилання зворотного дзвінка, яке ви передаєте у свою функцію, є посиланням на функцію, як і будь-яке інше, ви можете робити всі звичайні речі з ним.
TJ Crowder

4
@everyone, який відповів: Я думаю, що проблема TiTaN полягає в тому, що він не знає, як передавати функцію, яка вимагає аргументів, у зворотний виклик, який не передає жодних аргументів. Подумайте setTimeout(). Відповідь - завершити зворотний виклик у doSomething(function(){foo('this','should','work')})
закритому режимі

Хтось вказує TiTaN на потік (бажано на SO), обговорюючи вищезгадану проблему, сьогодні мій пошук-фу слабкий.
slebetman

1
@Webwoman - Це залежить від вашого випадку використання. Ви можете передати його як аргумент або включити його до якогось об'єкта налаштувань / параметрів або будь-якого з кількох інших параметрів.
TJ Crowder

77

До того, як спробувати виконати, переконайтесь, що зворотний виклик є фактичною функцією:

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

  callback();
}

21
if(typeof callback == "function")матиме такий самий результат.
Реакційний

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

Якщо ви хочете виконати функцію, коли щось зроблено. Одне з хороших рішень - слухати події. Наприклад, я реалізую клас Dispatchera 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

Під час виклику функції зворотного дзвінка ми можемо використовувати її як нижче:

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

Зворотний виклик означає, що "Джейк" завжди додається користувачам перед тим, як відображати список користувачів із console.log.

Джерело (YouTube)


0

Спробуйте:

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

Функції - перший клас у JavaScript ; ви можете просто пропустити їх навколо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.