สร้างการโทรกลับที่กำหนดเองใน 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กำหนดไว้ มิฉะนั้นคุณจะได้รับข้อผิดพลาดที่บอกคุณว่าฟังก์ชั่นไม่ได้ถูกกำหนดไว้
J. Bruni

คำตอบ:


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
@ ทุกคนที่ตอบ: ฉันคิดว่าปัญหาของ TiTaN คือเขาไม่รู้วิธีส่งผ่านฟังก์ชั่นที่ต้องมีการโต้เถียงในการโทรกลับที่ไม่ผ่านการขัดแย้งใด ๆ setTimeout()คิด คำตอบคือปิดการโทรกลับในการปิด:doSomething(function(){foo('this','should','work')})
slebetman

มีคนชี้ 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
โปรดพิจารณาแก้ไขโพสต์ของคุณเพื่อเพิ่มคำอธิบายเพิ่มเติมเกี่ยวกับสิ่งที่รหัสของคุณทำและสาเหตุที่แก้ไขปัญหา คำตอบที่ส่วนใหญ่มีรหัส (แม้ว่าจะใช้งานได้) มักจะไม่ช่วยให้ OP เข้าใจปัญหาของพวกเขา อย่างไรก็ตามในกรณีนี้นี่เป็นคำถามเก่าแก่ที่มีคำตอบที่ได้รับการยอมรับอย่างสูงซึ่งโพสต์ไว้แล้วอาจไม่คุ้มค่ากับคุณในขณะที่ตอบคำถามนี้เมื่อมีคำถามที่ใหม่กว่าซึ่งสามารถทำได้ด้วยความสนใจมากขึ้น
SuperBiasedMan

1
ฉันชอบคำตอบนี้ที่แสดงถึงสิ่งที่ผู้คนต้องการเห็น
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

เมื่อเรียกฟังก์ชั่นการโทรกลับเราสามารถใช้มันเหมือนด้านล่าง:

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 ; คุณสามารถผ่านพวกเขาไปรอบ ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.