使用JavaScript创建自定义回调函数

343

我所需要做的就是在当前函数执行结束时执行回调函数。

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
必须在定义 function success 后才能调用 object.LoadData(success)。否则,你会收到一个错误提示,告诉你该函数未定义。 - J. Bruni
11个回答

595

实际上,你的代码基本上可以直接使用,只需将回调函数声明为一个参数,并使用该参数名称直接调用即可。

基础知识

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函数,最终弹出"stuff goes here"。

需要注意的是,传递函数时非常重要要传递函数引用foo),而不是调用函数并传递其结果(foo())。在您的问题中,您做得很好,但值得指出的是,这是一个常见错误。

更高级的内容

有时候您想要调用回调函数以便它看到this的特定值。您可以使用JavaScript的call函数轻松实现:

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:很奇怪,将参数传递到回调函数中并没有什么特别之处。你传递到函数中的回调引用就像其他任何函数引用一样,你可以对它进行所有正常的操作。 - T.J. Crowder
4
@所有回答的人:我认为TiTaN的问题在于他不知道如何将需要参数的函数传递到不传递任何参数的回调中。想一下setTimeout()。答案是将回调函数包装在闭包中:doSomething(function(){foo('this','should','work')}) - slebetman
有人能把 TiTaN 指向一个讨论上述问题的线程(最好是在 SO 上),我的搜索技巧今天很弱。 - slebetman
很酷的提示,将回调作为参数传递是强制性的还是一种良好实践,如果是后者,请说明原因。谢谢。 - Webwoman
1
@Webwoman - 这取决于您的使用情况。您可以将其作为参数传递,或将其包含在某种设置/选项对象中,或选择其他几个选项之一。 - T.J. Crowder

83

在尝试执行回调函数之前,确保回调函数是实际的函数是一个好的实践:

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

  callback();
}

21
如果 typeof callback == "function",将会得到相同的结果。 - Reactgular
24
是的,但如果没有回调函数,为什么要对其进行typeof检查呢?这才是callback && 的意义所在。 - theonlygusti

67

我的两分钱。同样但不同...

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


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

9
我喜欢这段代码,我一直在寻找它。 - vimal1083

13
function loadData(callback) {

    //execute other requirement

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

loadData(function(){

   //execute callback

});

6
请考虑编辑您的帖子,以增加关于您的代码是什么以及为什么它可以解决问题的更多说明。如果答案主要只包含代码(即使它能正常工作),通常无法帮助提问者理解他们的问题。但是,在这种情况下,这是一个非常古老的问题,已经发布了备受推崇的答案,如果有更新的问题需要更多关注,则可能不值得回答此问题。 - SuperBiasedMan
1
我喜欢这个答案,它直接演示了人们想要看到的内容。 - JΛYDΞV

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

有些答案虽然正确,但可能有点难以理解。以下是一个通俗易懂的例子:

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显示用户列表之前,总是将“Jake”添加到用户中。

来源(YouTube)


4

如果你想在某个操作完成时执行一个函数,监听事件是一个很好的解决方案。

例如,我将使用ES6实现一个Dispatcher,一个DispatcherEvent类,然后:

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

愉快地编码吧!

附言:我的代码缺少处理一些错误异常的部分。


2

在调用回调函数时,我们可以像下面这样使用它:

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
     function login(email, password, callback) {
         //verify the user
         const users = [
          { email: "abc@gmail.com", password: "123" },
          { email: "xyz@gmail.com", password: "xyz" }
         ];

         const user = users.find(
           (user) => user.email === email && user.password === password
         );
         callback(user);
       `enter code here`}

    function redirect(user) {
       if (user) {
          //user is successfully logged in
          console.log("user is successfully logged in ");
       } else {
         console.log("Incorrect credentials ");
       } 
    }

    
    login("abc@gmail.com", "123", redirect);

我希望这个例子能帮助每个想了解JS回调的人。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接