Hàm gọi lại là gì?


684

Hàm gọi lại là gì?


8
bạn có thể tìm thấy ở đây: stackoverflow.com/a/9652434/3343174 lời giải thích tốt nhất về cuộc gọi lại
Fakher


Nhìn vào câu trả lời thứ hai để được giải thích chi tiết
Donato

Giải thích tốt nhất về cuộc gọi lại mà tôi đã từng tìm thấy youtube.com/watch?v=xHneyv38Jro
Sameer Sinha

Câu trả lời:


680

Các nhà phát triển thường bị nhầm lẫn bởi những gì một cuộc gọi lại là vì tên của điều đáng nguyền rủa.

Hàm gọi lại là một hàm:

  • có thể truy cập bằng chức năng khác và
  • được gọi sau hàm đầu tiên nếu hàm đầu tiên đó hoàn thành

Một cách hay để tưởng tượng cách một hàm gọi lại hoạt động là nó là một hàm được " gọi ở phía sau " của hàm mà nó được truyền vào.

Có lẽ một cái tên tốt hơn sẽ là một chức năng "gọi sau" .

Cấu trúc này rất hữu ích cho hành vi không đồng bộ nơi chúng tôi muốn một hoạt động diễn ra bất cứ khi nào một sự kiện trước đó hoàn thành.

Mã giả:

// A function which accepts another function as an argument
// (and will automatically invoke that function when it completes - note that there is no explicit call to callbackFunction)
funct printANumber(int number, funct callbackFunction) {
    printout("The number you provided is: " + number);
}

// a function which we will use in a driver function as a callback function
funct printFinishMessage() {
    printout("I have finished printing numbers.");
}

// Driver method
funct event() {
   printANumber(6, printFinishMessage);
}

Kết quả nếu bạn gọi sự kiện ():

The number you provided is: 6
I have finished printing numbers.

Thứ tự của đầu ra ở đây là quan trọng. Vì các hàm gọi lại được gọi sau đó, "Tôi đã in xong số" được in lần cuối chứ không phải trước.

Các cuộc gọi lại được gọi là do cách sử dụng của chúng với các ngôn ngữ con trỏ. Nếu bạn không sử dụng một trong số đó, đừng chuyển sang tên 'gọi lại'. Chỉ cần hiểu rằng đó chỉ là một tên để mô tả một phương thức được cung cấp làm đối số cho phương thức khác, sao cho khi phương thức cha được gọi (bất kỳ điều kiện nào, chẳng hạn như nhấp vào nút, đánh dấu thời gian, v.v.) và thân phương thức của nó hoàn thành, chức năng gọi lại sau đó được gọi.

Một số ngôn ngữ hỗ trợ cấu trúc trong đó nhiều đối số chức năng gọi lại được hỗ trợ và được gọi dựa trên cách hàm cha hoàn thành (nghĩa là một cuộc gọi lại được gọi trong trường hợp hàm cha hoàn thành thành công, một hàm khác được gọi trong trường hợp hàm cha ném ra lỗi cụ thể, vv).


31
Ví dụ của bạn rất hay nhưng tôi không hiểu tại sao thuật ngữ này là "gọi lại". Khi nào có nghĩa là "gọi lại"?
CodyBugstein

4
Xin chào, về once its parent method completes, the function which this argument represents is then called. Vì vậy, nếu hàm được truyền cho hàm khác làm đối số nhưng được gọi từ giữa thời gian chạy của hàm cha như thế parent(cb) {dostuff1(); cb(); dostuff2()}thì nó không được coi là callbackhàm?
Max Yari

2
@MaxYari: IMHO, nó vẫn được coi là gọi lại. Điều quan trọng ở đây là funcin cha sẽ sử dụng chức năng đầu vào (còn gọi là gọi lại) bằng cách nào đó. Nó có thể được gọi ở giữa hoặc ở cuối hoặc nếu một điều kiện được đáp ứng.
Kamran Bigdely

12
@ 8bitjunkie cảm ơn bạn - nhưng phương thức meanOfLife được gọi trong hàm printANumber ở đâu?
BKSpurgeon

2
Điều này hoàn toàn không đúng: "được gọi tự động sau khi chức năng đầu tiên hoàn thành". Một cuộc gọi lại hoàn toàn không phải thực hiện, hãy để một mình tự động. Trong thực tế, không có gì lạ khi các cuộc gọi lại hoàn thành trước khi chức năng cha hoàn thành. Tôi rất không thích cách mọi người mô tả các cuộc gọi lại là các hàm được thực thi "sau này". Nó rất khó hiểu với những người đang tìm hiểu về họ. Nói một cách đơn giản, các cuộc gọi lại chỉ là các hàm được truyền vào dưới dạng đối số cho các hàm khác. Giai đoạn = Stage. Một lời giải thích tốt hơn sẽ bao gồm giải thích TẠI SAO các cuộc gọi lại qua các tham chiếu hàm.
Jordan

224

Định nghĩa mờ

Hàm gọi lại là một hàm bạn cung cấp cho một đoạn mã khác, cho phép nó được gọi bởi mã đó.

Ví dụ

Tại sao bạn muốn làm điều này? Giả sử có một dịch vụ bạn cần gọi. Nếu dịch vụ trả về ngay lập tức, bạn chỉ cần:

  1. Gọi nó đi
  2. Chờ kết quả
  3. Tiếp tục một khi kết quả đến

Ví dụ, giả sử dịch vụ là factorialchức năng. Khi bạn muốn giá trị của 5!, bạn sẽ gọi factorial(5)và các bước sau sẽ xảy ra:

  1. Vị trí thực hiện hiện tại của bạn được lưu (trên ngăn xếp, nhưng điều đó không quan trọng)

  2. Thi công được bàn giao cho factorial

  3. Khi factorialhoàn thành, nó đặt kết quả ở đâu đó bạn có thể nhận được

  4. Thi hành trở lại nơi nó đã ở [1]

Bây giờ giả sử factorialmất một thời gian rất dài, bởi vì bạn đang cho nó những con số khổng lồ và nó cần phải chạy trên một số cụm siêu máy tính ở bất cứ đâu. Giả sử bạn mong đợi sẽ mất 5 phút để trả về kết quả của bạn. Bạn có thể:

  1. Giữ thiết kế của bạn và chạy chương trình của bạn vào ban đêm khi bạn ngủ, để bạn không nhìn chằm chằm vào màn hình một nửa thời gian

  2. Thiết kế chương trình của bạn để làm những việc khác trong khi factorialđang làm việc của nó

Nếu bạn chọn tùy chọn thứ hai, thì cuộc gọi lại có thể phù hợp với bạn.

Thiết kế đầu cuối

Để khai thác mẫu gọi lại, điều bạn muốn là có thể gọi factorialtheo cách sau:

factorial(really_big_number, what_to_do_with_the_result)

Tham số thứ hai what_to_do_with_the_result, là một hàm bạn gửi cùng factorial, với hy vọng factorialsẽ gọi nó trên kết quả của nó trước khi trả về.

Vâng, điều này có nghĩa là factorialcần phải được viết để hỗ trợ các cuộc gọi lại.

Bây giờ giả sử rằng bạn muốn có thể truyền tham số cho cuộc gọi lại của mình. Bây giờ bạn không thể, bởi vì bạn sẽ không được gọi nó, factoriallà. Vì vậy, factorialcần phải được viết để cho phép bạn truyền tham số của mình và nó sẽ chuyển chúng cho cuộc gọi lại của bạn khi nó gọi nó. Nó có thể trông như thế này:

factorial (number, callback, params)
{
    result = number!   // i can make up operators in my pseudocode
    callback (result, params)
}

Bây giờ factorialcho phép mẫu này, cuộc gọi lại của bạn có thể trông như thế này:

logIt (number, logger)
{
    logger.log(number)
}

và cuộc gọi của bạn factorialsẽ là

factorial(42, logIt, logger)

Điều gì nếu bạn muốn trả lại một cái gì đó từ logIt? Chà, bạn không thể, vì factorialkhông chú ý đến nó.

Chà, tại sao không thể factorialtrả lại những gì cuộc gọi lại của bạn?

Làm cho nó không chặn

Vì việc thực thi có nghĩa là được bàn giao cho cuộc gọi lại khi factorialkết thúc, nên thực sự không nên trả lại bất cứ điều gì cho người gọi. Và lý tưởng, bằng cách nào đó, nó sẽ khởi động công việc của nó trong một luồng / quy trình / máy khác và quay lại ngay lập tức để bạn có thể tiếp tục, có thể là một cái gì đó như thế này:

factorial(param_1, param_2, ...)
{
    new factorial_worker_task(param_1, param_2, ...);
    return;
}

Đây bây giờ là một "cuộc gọi không đồng bộ", có nghĩa là khi bạn gọi nó, nó sẽ trả về ngay lập tức nhưng chưa thực sự hoàn thành công việc của mình. Vì vậy, bạn cần có các cơ chế để kiểm tra nó và để có được kết quả của nó khi nó kết thúc và chương trình của bạn đã trở nên phức tạp hơn trong quá trình này.

Và nhân tiện, sử dụng mẫu này factorial_worker_taskcó thể khởi chạy lại cuộc gọi lại của bạn một cách không đồng bộ và trả về ngay lập tức.

Vậy bạn làm gì?

Câu trả lời là ở trong mẫu gọi lại. Bất cứ khi nào bạn muốn viết

a = f()
g(a)

fđược gọi là không đồng bộ, thay vào đó bạn sẽ viết

f(g)

nơi gđược thông qua như một cuộc gọi lại.

Điều này về cơ bản thay đổi cấu trúc liên kết dòng chảy của chương trình của bạn và làm cho một số quen dần.

Ngôn ngữ lập trình của bạn có thể giúp bạn rất nhiều bằng cách cung cấp cho bạn cách tạo chức năng nhanh chóng. Trong mã ngay trên, hàm gcó thể nhỏ như print (2*a+1). Nếu ngôn ngữ của bạn yêu cầu bạn xác định đây là một chức năng riêng biệt, với một tên và chữ ký hoàn toàn không cần thiết, thì cuộc sống của bạn sẽ trở nên khó chịu nếu bạn sử dụng mô hình này rất nhiều.

Mặt khác, nếu ngôn ngữ của bạn cho phép bạn tạo ra lambdas, thì bạn đang ở trong tình trạng tốt hơn nhiều. Sau đó bạn sẽ viết một cái gì đó như

f( func(a) { print(2*a+1); })

cái nào đẹp hơn nhiều

Làm thế nào để vượt qua cuộc gọi lại

Làm thế nào bạn sẽ vượt qua chức năng gọi lại factorial? Vâng, bạn có thể làm điều đó theo một số cách.

  1. Nếu hàm được gọi đang chạy trong cùng một quy trình, bạn có thể vượt qua một con trỏ hàm

  2. Hoặc có thể bạn muốn duy trì một từ điển fn name --> fn ptrtrong chương trình của mình, trong trường hợp đó bạn có thể chuyển tên

  3. Có thể ngôn ngữ của bạn cho phép bạn xác định chức năng tại chỗ, có thể là lambda! Trong nội bộ, nó đang tạo ra một số loại đối tượng và vượt qua một con trỏ, nhưng bạn không phải lo lắng về điều đó.

  4. Có lẽ chức năng bạn đang gọi đang chạy trên một máy hoàn toàn riêng biệt và bạn đang gọi nó bằng giao thức mạng như HTTP. Bạn có thể hiển thị cuộc gọi lại của mình dưới dạng hàm có thể gọi HTTP và chuyển URL của nó.

Bạn có được ý tưởng.

Sự gia tăng gần đây của cuộc gọi lại

Trong thời đại web chúng tôi đã tham gia, các dịch vụ chúng tôi gọi thường qua mạng. Chúng tôi thường không có bất kỳ sự kiểm soát nào đối với các dịch vụ đó, ví dụ như chúng tôi không viết chúng, chúng tôi không duy trì chúng, chúng tôi không thể đảm bảo chúng hoạt động hay cách chúng hoạt động.

Nhưng chúng tôi không thể hy vọng các chương trình của mình bị chặn trong khi chúng tôi chờ đợi các dịch vụ này phản hồi. Nhận thức được điều này, các nhà cung cấp dịch vụ thường thiết kế API bằng cách sử dụng mẫu gọi lại.

JavaScript hỗ trợ các cuộc gọi lại rất độc đáo, ví dụ như với lambdas và các bao đóng. Và có rất nhiều hoạt động trong thế giới JavaScript, cả trên trình duyệt cũng như trên máy chủ. Thậm chí có những nền tảng JavaScript đang được phát triển cho thiết bị di động.

Khi chúng ta tiến lên phía trước, ngày càng nhiều người trong chúng ta sẽ viết mã không đồng bộ, theo đó sự hiểu biết này sẽ rất cần thiết.


1
Một khái niệm được giải thích rất tốt ..! :)
piyushGidel 15/05/2015

Giải thích tuyệt vời, +1.
Lingamurthy CS

1
Đây là câu trả lời tốt nhất trong số tất cả những người khác. Vui lòng upvote nó.
Abhishek Nalin

3
Giải thích hoàn hảo và mọi thứ được giải thích. Tôi ước tôi có thể nâng cấp nó một lần nữa.
Yogesh Yadav

Vâng, tôi hiểu làm thế nào lambas hoạt động trong javascript và ruby. Và java 8 nhưng các phiên bản cũ của java đã không sử dụng lambas và thay vào đó sử dụng các lớp và tôi muốn biết cách thức gọi lại đó hoạt động như thế nào. Vẫn là một câu trả lời vượt trội cho người khác.
Donato

96

Lưu ý rằng gọi lại là một từ.

Trang gọi lại wikipedia giải thích nó rất tốt.

trích dẫn từ trang wikipedia:

Trong lập trình máy tính, một cuộc gọi lại là một tham chiếu đến mã thực thi hoặc một đoạn mã thực thi, được truyền dưới dạng đối số cho mã khác. Điều này cho phép lớp phần mềm cấp thấp hơn gọi một chương trình con (hoặc hàm) được xác định trong lớp cấp cao hơn.


14
Cách tốt đẹp để trình bày một câu trả lời.
Chathuranga Chandrasekara

1
Và điều này cũng dẫn đến câu trả lời theo một cách khác. Danh từ "gọi lại" là cái đã được "gọi lại", giống như cách mà một cái gì đó đã trải qua tắt máy đã bị tắt và một cái gì đó được sử dụng để đăng nhập là một thông tin đăng nhập.
Ẩn danh

22
Đây có thể là một nhận xét - về cơ bản đó là một liên kết đến Wikipedia
CodyBugstein

Wikipedia thực sự đã có một số công cụ lập trình thực sự tuyệt vời trong các bếp của nó. Tôi luôn cảm thấy như thuật ngữ "gọi lại" được giải thích tốt nhất bằng cách sử dụng cụm từ "Tôi sẽ gọi lại cho ..."
Thomas

Giải thích tuyệt vời tại javascriptissexy.com/ trên; mà tôi sẽ đăng lại ở đây; Hàm gọi lại là một hàm được truyền cho hàm khác dưới dạng tham số và hàm gọi lại được gọi hoặc được thực thi bên trong Hàm khác. // Lưu ý rằng mục trong tham số của phương thức nhấp là một hàm, không phải là biến. // Mục này là hàm gọi lại $ ("# btn_1"). Click (function () {alert ("Btn 1 Clicked" );}); Như bạn thấy trong ví dụ trước, chúng ta truyền một hàm làm tham số cho phương thức nhấp để nó thực thi -
MarcoZen

46

Phản hồi của cư sĩ sẽ là chức năng không phải do bạn gọi mà là bởi người dùng hoặc trình duyệt sau khi một sự kiện nào đó đã xảy ra hoặc sau khi một số mã được xử lý.


42

Hàm gọi lại là một hàm nên được gọi khi một điều kiện nhất định được đáp ứng. Thay vì được gọi ngay lập tức, chức năng gọi lại được gọi tại một thời điểm nhất định trong tương lai.

Thông thường, nó được sử dụng khi một tác vụ đang được bắt đầu sẽ kết thúc không đồng bộ (tức là sẽ kết thúc một thời gian sau khi chức năng gọi trở lại).

Ví dụ: một chức năng để yêu cầu một trang web có thể yêu cầu người gọi của nó cung cấp chức năng gọi lại sẽ được gọi khi trang web đã tải xuống xong.


Trong câu đầu tiên của bạn, bạn nói "...when a condition is met"nhưng tôi nghĩ các cuộc gọi lại được gọi khi hàm cha kết thúc thực thi và không phụ thuộc vào các điều kiện (?).
Ojonugwa Jude Ochalifu

"Điều kiện nhất định" chỉ có nghĩa là họ thường được gọi vì một lý do, thay vì ngẫu nhiên. Một cuộc gọi lại có thể được gọi khi cha mẹ / người tạo vẫn đang thực thi - điều này có thể dẫn đến một điều kiện cuộc đua nếu lập trình viên không mong đợi nó.
Thomas Bratt

Được rồi. Cảm ơn đã làm rõ
Ojonugwa Jude Ochalifu

34

Cuộc gọi lại được mô tả dễ dàng nhất trong điều khoản của hệ thống điện thoại. Một cuộc gọi chức năng tương tự như gọi ai đó qua điện thoại, hỏi cô ấy một câu hỏi, nhận được câu trả lời và gác máy; thêm một cuộc gọi lại thay đổi sự tương tự để sau khi hỏi cô ấy một câu hỏi, bạn cũng cho cô ấy biết tên và số của cô ấy để cô ấy có thể gọi lại cho bạn với câu trả lời.

- Paul Jakubik, "Thực hiện gọi lại trong C ++"


1
Vậy tên và số của tôi là một hàm?
Koray Tugay

Không, đó sẽ là tương tự nếu "gọi lại" là một tên hay cho thay vào đó: bạn yêu cầu nhà điều hành điện thoại thực hiện cuộc gọi. Kết thúc.
gherson 18/03/19

33

Tôi tin rằng thuật ngữ "gọi lại" này đã bị sử dụng nhầm ở rất nhiều nơi. Định nghĩa của tôi sẽ là một cái gì đó như:

Hàm gọi lại là một hàm mà bạn chuyển cho ai đó và để họ gọi nó vào một lúc nào đó.

Tôi nghĩ mọi người chỉ cần đọc câu đầu tiên của định nghĩa wiki:

một cuộc gọi lại là một tham chiếu đến mã thực thi hoặc một đoạn mã thực thi được truyền dưới dạng đối số cho mã khác.

Tôi đã làm việc với rất nhiều API, thấy nhiều ví dụ tồi tệ khác nhau. Nhiều người có xu hướng đặt tên con trỏ hàm (tham chiếu đến mã thực thi) hoặc hàm ẩn danh (một đoạn mã thực thi) "gọi lại", nếu chúng chỉ là các hàm tại sao bạn cần một tên khác cho điều này?

Trên thực tế chỉ có câu thứ hai trong định nghĩa wiki cho thấy sự khác biệt giữa chức năng gọi lại và chức năng bình thường:

Điều này cho phép lớp phần mềm cấp thấp hơn gọi một chương trình con (hoặc hàm) được xác định trong lớp cấp cao hơn.

Vì vậy, sự khác biệt là người mà bạn sẽ vượt qua chức năng và cách chức năng được thông qua của bạn sẽ được gọi. Nếu bạn chỉ định nghĩa một hàm và chuyển nó đến một hàm khác và gọi nó trực tiếp trong thân hàm đó, đừng gọi nó là một hàm gọi lại. Định nghĩa cho biết chức năng được thông qua của bạn sẽ được gọi bởi chức năng "cấp thấp hơn".

Tôi hy vọng mọi người có thể ngừng sử dụng từ này trong bối cảnh mơ hồ, nó không thể giúp mọi người hiểu rõ hơn chỉ tệ hơn.


2
Câu trả lời của bạn có ý nghĩa ... nhưng tôi gặp khó khăn khi hình dung nó. Bạn có thể đưa ra một ví dụ không?
CodyBugstein

3
@Zane Wong :: Cuối cùng bạn đã viết "Định nghĩa cho biết chức năng được thông qua của bạn sẽ được gọi bởi chức năng" cấp thấp hơn "." Bạn có thể giải thích những gì chức năng cấp thấp hơn chỉ ra? Nó tốt hơn nếu bạn đưa ra một ví dụ.
Viku

Một ví dụ sẽ rất hay
Yousuf Azad

1
Tôi nghĩ rằng sự khác biệt giữa kiểu gọi hàm cổ điển và kiểu gọi lại là liên kết với hướng phụ thuộc: nếu mô-đun A phụ thuộc vào ("sử dụng") mô-đun B, A gọi hàm của B, đó không phải là gọi lại. Nếu A chuyển tham chiếu đến chức năng của mình đến B, thì B gọi hàm của A, đây là cuộc gọi lại: cuộc gọi đi ngược lại so với phụ thuộc mô-đun.
XouDo

30

Hãy giữ nó đơn giản. Chức năng gọi lại là gì?

Ví dụ bởi Dụ ngôn và Tương tự

Tôi có một thư ký. Mỗi ngày tôi yêu cầu cô ấy: (i) bỏ thư gửi đi của công ty tại bưu điện, và sau khi cô ấy làm điều đó, hãy làm: (ii) bất cứ nhiệm vụ nào tôi đã viết cho cô ấy trên một trong những ghi chú dán đó .

Bây giờ, nhiệm vụ trên ghi chú là gì? Nhiệm vụ thay đổi theo từng ngày.

Giả sử vào ngày đặc biệt này, tôi yêu cầu cô ấy in ra một số tài liệu. Vì vậy, tôi viết nó xuống tờ giấy dính, và tôi ghim nó trên bàn của cô ấy cùng với thư gửi đi mà cô ấy cần gửi.

Tóm tắt:

  1. Đầu tiên, cô ấy cần bỏ thư và
  2. Ngay sau khi xong, cô cần in ra một số tài liệu.

Chức năng gọi lại là nhiệm vụ thứ hai: in ra những tài liệu đó. Bởi vì nó được thực hiện SAU khi thư bị bỏ đi, và cũng vì ghi chú dán yêu cầu cô ấy in tài liệu được đưa cho cô ấy cùng với thư cô ấy cần gửi.

Bây giờ chúng ta hãy gắn kết điều này với từ vựng lập trình

  • Tên phương thức trong trường hợp này là: Drop OfferMail.
  • Và chức năng gọi lại là: Print OfferDocument. Print OfferDocument là chức năng gọi lại vì chúng tôi muốn thư ký thực hiện việc đó, chỉ sau khi Drop OfferMail chạy.
  • Vì vậy, tôi sẽ "chuyển: Print OfferDocument như một" đối số "cho phương thức DropPackMail. Đây là một điểm quan trọng.

Đó là tất cả. Chỉ có bấy nhiêu thôi. Tôi hy vọng điều đó đã xóa nó cho bạn - và nếu không, hãy đăng bình luận và tôi sẽ làm hết sức mình để làm rõ.


18

Điều này làm cho các cuộc gọi lại nghe giống như câu lệnh return ở cuối phương thức.

Tôi không chắc đó là cái gì.

Tôi nghĩ rằng Callbacks thực sự là một cuộc gọi đến một chức năng, do hậu quả của một chức năng khác được gọi và hoàn thành.

Tôi cũng nghĩ rằng Callbacks có nghĩa là để giải quyết việc gọi ban đầu, theo kiểu "hey! Điều mà bạn yêu cầu? Tôi đã thực hiện nó - chỉ nghĩ rằng tôi sẽ cho bạn biết - quay lại với bạn".


1
+1 để đặt câu hỏi Callbacks vs Return. Tôi đã từng bị cuốn theo điều này và nhiều sinh viên tốt nghiệp mà tôi làm việc cùng cũng vậy.
8bitjunkie

2
Câu trả lời hay - đã giúp tôi hiểu nó không giống như nhiều câu trả lời khác!
adaam

18

Gọi lại là gì?

  • Nói chung, một cuộc gọi điện thoại được thực hiện để trả lại một cuộc gọi mà ai đó đã nhận được.
  • Trong điện toán, một cuộc gọi lại là một đoạn mã thực thi được truyền dưới dạng đối số cho mã khác. Khi chức năng được thực hiện với công việc của nó (hoặc khi một số sự kiện xảy ra), nó sẽ gọi lại cho bạn (nó gọi lại cho bạn - do đó là tên).

A là gì chức năng gọi lại ?

  • một chức năng gọi lại giống như một Servant "gọi lại" cho Master của mình khi anh ta đã hoàn thành một nhiệm vụ.
  • một hàm gọi lại là một hàm được truyền cho một hàm khác (hãy gọi hàm này khác otherFunction) làm tham số và hàm gọi lại được gọi (hoặc được thực thi) bên trong otherFunction.
    function action(x, y, callback) {
        return callback(x, y);
    }

    function multiplication(x, y) {
        return x * y;
    }

    function addition(x, y) {
        return x + y;
    }

    alert(action(10, 10, multiplication)); // output: 100

    alert(action(10, 10, addition)); // output: 20

Trong SOA, gọi lại cho phép các Mô-đun Plugin truy cập các dịch vụ từ vùng chứa / môi trường.

Tương tự: Gọi lại. Không đồng bộ. Không chặn
Ví dụ thực tế cho cuộc gọi lại


Hàm gọi lại không phải là hàm bậc cao. Nó được chuyển đến một hàm bậc cao hơn.
danio

17

Call After sẽ là một cái tên tốt hơn so với tên ngu ngốc, gọi lại . Khi hoặc nếu điều kiện được đáp ứng trong một hàm, hãy gọi một hàm khác, hàm Call After , hàm được nhận làm đối số.

Thay vì mã cứng một chức năng bên trong một hàm, người ta viết một hàm để chấp nhận hàm Call After đã được viết thành đối số. Cuộc gọi sau có thể được gọi dựa trên các thay đổi trạng thái được phát hiện bởi mã trong hàm nhận đối số.


Ý kiến ​​hay. Tôi đã đi đến "được gọi ở phía sau" để thử và giải thích điều này. Tôi có thể thấy ai đó như Martin Fowler phổ biến "cuộc gọi sau" như một thuật ngữ mới cho những điều này trên blog bán kết của mình.
8bitjunkie

15

Hàm gọi lại là một hàm bạn chỉ định cho một hàm / phương thức hiện có, được gọi khi một hành động được hoàn thành, yêu cầu xử lý bổ sung, v.v.

Ví dụ, trong Javascript, hay cụ thể hơn là jQuery, bạn có thể chỉ định một đối số gọi lại sẽ được gọi khi hoạt ảnh kết thúc.

Trong PHP, preg_replace_callback()hàm cho phép bạn cung cấp một hàm sẽ được gọi khi biểu thức chính quy được khớp, truyền (các) chuỗi khớp với nhau làm đối số.


10

nhìn vào hình ảnh :)Đây là cách nó hoạt động

Chương trình chính gọi hàm thư viện (cũng có thể là hàm cấp hệ thống) với tên hàm gọi lại. Chức năng gọi lại này có thể được thực hiện theo nhiều cách. Chương trình chính chọn một cuộc gọi lại theo yêu cầu.

Cuối cùng, hàm thư viện gọi hàm gọi lại trong khi thực thi.


7
Bạn có phiền cũng thêm một lời giải thích văn bản cho điều này? Nếu hình ảnh biến mất, câu trả lời này mất tất cả bối cảnh.
Tim Post

văn bản từ những người khác giải thích nó là tốt nhất. điều duy nhất tôi cảm thấy thiếu là hình ảnh :)

Trong tất cả các mô tả dài dòng tôi đã thấy ở đây, đây là một trong những mô tả khiến tôi phải thốt lên "ahhhhh, bây giờ tôi thấy công dụng của nó." Có một upvote.
DiBosco

7

Câu trả lời đơn giản cho câu hỏi này là hàm gọi lại là một hàm được gọi thông qua một con trỏ hàm. Nếu bạn truyền con trỏ (địa chỉ) của hàm làm đối số cho đối số khác, khi con trỏ đó được sử dụng để gọi hàm, nó trỏ đến nó sẽ nói rằng một cuộc gọi lại được thực hiện


6

Giả sử chúng ta có một hàm sort(int *arraytobesorted,void (*algorithmchosen)(void))trong đó nó có thể chấp nhận một con trỏ hàm làm đối số của nó có thể được sử dụng tại một số điểm trong sort()quá trình thực hiện. Sau đó, ở đây mã được xử lý bởi con trỏ hàm algorithmchosenđược gọi là hàm gọi lại .

Và thấy ưu điểm là chúng ta có thể chọn bất kỳ thuật toán nào như:

  1.    algorithmchosen = bubblesort
  2.    algorithmchosen = heapsort
  3.    algorithmchosen = mergesort   ...

Điều đó đã được nói, đã được thực hiện với nguyên mẫu:

  1.   `void bubblesort(void)`
  2.   `void heapsort(void)`
  3.   `void mergesort(void)`   ...

Đây là một khái niệm được sử dụng để đạt được tính đa hình trong lập trình hướng đối tượng


Giải thích tuyệt vời tại javascriptissexy.com/ trên; mà tôi sẽ đăng lại ở đây; Hàm gọi lại là một hàm được truyền cho hàm khác dưới dạng tham số và hàm gọi lại được gọi hoặc được thực thi bên trong Hàm khác. // Lưu ý rằng mục trong tham số của phương thức nhấp là một hàm, không phải là biến. // Mục này là hàm gọi lại $ ("# btn_1"). Click (function () {alert ("Btn 1 Clicked" );}); Như bạn thấy trong ví dụ trước, chúng ta truyền một hàm làm tham số cho phương thức nhấp để nó thực thi -
MarcoZen

4

Trong lập trình máy tính, một cuộc gọi lại là một tham chiếu đến mã thực thi, hoặc một đoạn mã thực thi, được truyền dưới dạng đối số cho mã khác. Điều này cho phép lớp phần mềm cấp thấp hơn gọi một chương trình con (hoặc hàm) được xác định trong lớp cấp cao hơn. - Wikipedia

Gọi lại trong C bằng cách sử dụng con trỏ hàm

Trong C, gọi lại được thực hiện bằng cách sử dụng Hàm con trỏ. Con trỏ hàm - như tên cho thấy, là một con trỏ tới hàm.

Ví dụ: int (* ptrFunc) ();

Ở đây, ptrFunc là một con trỏ tới một hàm không có đối số và trả về một số nguyên. KHÔNG quên đặt trong ngoặc đơn, nếu không trình biên dịch sẽ cho rằng ptrFunc là tên hàm bình thường, không mất gì và trả về một con trỏ tới một số nguyên.

Đây là một số mã để chứng minh con trỏ hàm.

#include<stdio.h>
int func(int, int);
int main(void)
{
    int result1,result2;
    /* declaring a pointer to a function which takes
       two int arguments and returns an integer as result */
    int (*ptrFunc)(int,int);

    /* assigning ptrFunc to func's address */                    
    ptrFunc=func;

    /* calling func() through explicit dereference */
    result1 = (*ptrFunc)(10,20);

    /* calling func() through implicit dereference */        
    result2 = ptrFunc(10,20);            
    printf("result1 = %d result2 = %d\n",result1,result2);
    return 0;
}

int func(int x, int y)
{
    return x+y;
}

Bây giờ chúng ta hãy cố gắng hiểu khái niệm Callback trong C bằng cách sử dụng con trỏ hàm.

Chương trình hoàn chỉnh có ba tệp: callback.c, reg_callback.h và reg_callback.c.

/* callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* callback function definition goes here */
void my_callback(void)
{
    printf("inside my_callback\n");
}

int main(void)
{
    /* initialize function pointer to
    my_callback */
    callback ptr_my_callback=my_callback;                        
    printf("This is a program demonstrating function callback\n");
    /* register our callback function */
    register_callback(ptr_my_callback);                          
    printf("back inside main program\n");
    return 0;
}

/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);


/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
    printf("inside register_callback\n");
    /* calling our callback function my_callback */
    (*ptr_reg_callback)();                               
}

Nếu chúng tôi chạy chương trình này, đầu ra sẽ là

Đây là một chương trình thể hiện chức năng gọi lại bên trong register_callback bên trong my_callback trở lại bên trong chương trình chính

Hàm lớp cao hơn gọi hàm lớp thấp hơn như một cuộc gọi bình thường và cơ chế gọi lại cho phép hàm lớp thấp hơn gọi hàm lớp cao hơn thông qua một con trỏ đến hàm gọi lại.

Gọi lại trong Java bằng giao diện

Java không có khái niệm về con trỏ hàm. Nó thực hiện cơ chế gọi lại thông qua cơ chế Giao diện của nó Ở đây thay vì một con trỏ hàm, chúng ta khai báo một Giao diện có một phương thức sẽ được gọi khi callee hoàn thành nhiệm vụ của nó

Hãy để tôi chứng minh điều đó qua một ví dụ:

Giao diện gọi lại

public interface Callback
{
    public void notify(Result result);
}

Người gọi hoặc Lớp cấp cao hơn

public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee

//Other functionality
//Call the Asynctask
ce.doAsynctask();

public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}

Hàm Callee hoặc lớp dưới

public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}

doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}

Gọi lại bằng cách sử dụng mẫu EventListener

  • Danh sách mục

Mẫu này được sử dụng để thông báo 0 đến n số lượng Người quan sát / Người nghe rằng một nhiệm vụ cụ thể đã kết thúc

  • Danh sách mục

Sự khác biệt giữa cơ chế gọi lại và cơ chế EventListener / Observer là trong cuộc gọi lại, callee thông báo cho người gọi duy nhất, trong khi trong Eventlisener / Observer, callee có thể thông báo cho bất kỳ ai quan tâm đến sự kiện đó (thông báo có thể đi đến một số phần khác của ứng dụng chưa kích hoạt tác vụ)

Hãy để tôi giải thích nó thông qua một ví dụ.

Giao diện sự kiện

public interface Events {

public void clickEvent();
public void longClickEvent();
}

Lớp học Widget

package com.som_itsolutions.training.java.exampleeventlistener;

import java.util.ArrayList;
import java.util.Iterator;

public class Widget implements Events{

    ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>(); 
    ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();

    @Override
    public void clickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnClickEventListener> it = mClickEventListener.iterator();
                while(it.hasNext()){
                    OnClickEventListener li = it.next();
                    li.onClick(this);
                }   
    }
    @Override
    public void longClickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
        while(it.hasNext()){
            OnLongClickEventListener li = it.next();
            li.onLongClick(this);
        }

    }

    public interface OnClickEventListener
    {
        public void onClick (Widget source);
    }

    public interface OnLongClickEventListener
    {
        public void onLongClick (Widget source);
    }

    public void setOnClickEventListner(OnClickEventListener li){
        mClickEventListener.add(li);
    }
    public void setOnLongClickEventListner(OnLongClickEventListener li){
        mLongClickEventListener.add(li);
    }
}

Nút lớp

public class Button extends Widget{
private String mButtonText;
public Button (){
} 
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}

Hộp kiểm tra lớp

public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}

Lớp hoạt động

gói com.som_itsolutions.training.java.exampleeventlistener;

public class Activity implements Widget.OnClickEventListener
{
    public Button mButton;
    public CheckBox mCheckBox;
    private static Activity mActivityHandler;
    public static Activity getActivityHandle(){
        return mActivityHandler;
    }
    public Activity ()
    {
        mActivityHandler = this;
        mButton = new Button();
        mButton.setOnClickEventListner(this);
        mCheckBox = new CheckBox();
        mCheckBox.setOnClickEventListner(this);
        } 
    public void onClick (Widget source)
    {
        if(source == mButton){
            mButton.setButtonText("Thank you for clicking me...");
            System.out.println(((Button) mButton).getButtonText());
        }
        if(source == mCheckBox){
            if(mCheckBox.isChecked()==false){
                mCheckBox.setCheck(true);
                System.out.println("The checkbox is checked...");
            }
            else{
                mCheckBox.setCheck(false);
                System.out.println("The checkbox is not checked...");
            }       
        }
    }
    public void doSomeWork(Widget source){
        source.clickEvent();
    }   
}

Lớp khác

public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event                        //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}

Lớp chính

public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}

Như bạn có thể thấy từ đoạn mã trên, chúng ta có một giao diện gọi là các sự kiện về cơ bản liệt kê tất cả các sự kiện có thể xảy ra cho ứng dụng của chúng ta. Lớp Widget là lớp cơ sở cho tất cả các thành phần UI như Nút, Hộp kiểm. Các thành phần UI này là các đối tượng thực sự nhận các sự kiện từ mã khung. Lớp widget thực hiện giao diện Sự kiện và cũng có hai giao diện lồng nhau là OnClickEventListener & OnLongClickEventListener

Hai giao diện này chịu trách nhiệm lắng nghe các sự kiện có thể xảy ra trên các thành phần UI có nguồn gốc Widget như Nút hoặc Hộp kiểm. Vì vậy, nếu chúng ta so sánh ví dụ này với ví dụ Gọi lại trước đó bằng Giao diện Java, hai giao diện này hoạt động như giao diện Gọi lại. Vì vậy, mã cấp cao hơn (Hoạt động ở đây) thực hiện hai giao diện này. Và bất cứ khi nào một sự kiện xảy ra với một widget, mã cấp cao hơn (hoặc phương thức của các giao diện này được triển khai ở mã cấp cao hơn, ở đây là Activity) sẽ được gọi.

Bây giờ hãy để tôi thảo luận về sự khác biệt cơ bản giữa mẫu Callback và Eventlistener. Như chúng tôi đã đề cập rằng sử dụng Callback, Callee chỉ có thể thông báo cho một Người gọi duy nhất. Nhưng trong trường hợp mẫu EventListener, bất kỳ phần hoặc lớp nào khác của Ứng dụng đều có thể đăng ký các sự kiện có thể xảy ra trên Nút hoặc Hộp kiểm. Ví dụ về loại lớp này là OtherClass. Nếu bạn thấy mã của OtherClass, bạn sẽ thấy rằng nó đã tự đăng ký làm người nghe cho ClickEvent có thể xảy ra trong Nút được xác định trong Hoạt động. Điều thú vị là, bên cạnh Hoạt động (Người gọi), OtherClass này cũng sẽ được thông báo mỗi khi sự kiện nhấp xảy ra trên Nút.


Xin vui lòng tránh liên kết chỉ trả lời . Câu trả lời "hầu như không có nhiều hơn một liên kết đến một trang web bên ngoài có thể bị xóa .
Quentin

3

Hàm gọi lại là một hàm bạn truyền (dưới dạng tham chiếu hoặc con trỏ) cho một hàm hoặc đối tượng nhất định. Hàm hoặc đối tượng này sẽ gọi lại hàm này bất kỳ lúc nào, có thể nhiều lần, cho bất kỳ mục đích nào:

  • thông báo kết thúc một nhiệm vụ
  • yêu cầu so sánh giữa hai mục (như trong c qsort ())
  • báo cáo tiến độ của một quá trình
  • thông báo sự kiện
  • ủy thác việc xúi giục một đối tượng
  • ủy thác bức tranh của một khu vực

...

Vì vậy, mô tả một cuộc gọi lại như là một chức năng được gọi ở cuối của một chức năng hoặc tác vụ khác là quá đơn giản hóa (ngay cả khi đó là trường hợp sử dụng phổ biến).


2

Gọi lại là một ý tưởng chuyển một hàm làm tham số cho hàm khác và gọi hàm này sau khi quá trình hoàn tất.

Nếu bạn có được khái niệm gọi lại thông qua các câu trả lời tuyệt vời ở trên, tôi khuyên bạn nên tìm hiểu nền tảng của ý tưởng của nó.

"Điều gì đã khiến họ (Nhà khoa học máy tính) phát triển cuộc gọi lại?" Bạn có thể tìm hiểu một vấn đề, đó là chặn. (Đặc biệt là chặn UI) Và gọi lại không phải là giải pháp duy nhất cho nó. Có rất nhiều giải pháp khác (ví dụ: Chủ đề, Tương lai, Lời hứa ...).


1

Một lĩnh vực sử dụng quan trọng là bạn đăng ký một trong các chức năng của mình dưới dạng một điều khiển (tức là gọi lại) và sau đó gửi tin nhắn / gọi một số chức năng để thực hiện một số công việc hoặc xử lý. Bây giờ sau khi xử lý xong, hàm được gọi sẽ gọi hàm đã đăng ký của chúng tôi (tức là bây giờ gọi lại đã xong), do đó cho biết chúng tôi xử lý xong. Liên kết wikipedia
này giải thích khá tốt về mặt đồ họa.


1

Hàm gọi lại, còn được gọi là hàm bậc cao hơn, là hàm được truyền cho hàm khác dưới dạng tham số và hàm gọi lại được gọi (hoặc được thực thi) bên trong hàm cha.

$("#button_1").click(function() {
  alert("button 1 Clicked");
});

Ở đây chúng ta đã truyền một hàm làm tham số cho phương thức nhấp chuột. Và phương thức nhấp sẽ gọi (hoặc thực thi) hàm gọi lại mà chúng ta đã truyền cho nó.


1
Hàm gọi lại không phải là hàm bậc cao. Nó được chuyển đến một hàm bậc cao hơn.
danio

1

Hàm gọi lại Một hàm được truyền cho hàm khác làm đối số.

function test_function(){       
 alert("Hello world");  
} 

setTimeout(test_function, 2000);

Lưu ý: Trong ví dụ trên test_function được sử dụng làm đối số cho hàm setTimeout.


1
Chào mừng bạn đến với Stack Overflow! Trước khi trả lời một câu hỏi, luôn luôn đọc các câu trả lời hiện có. Câu trả lời này đã được cung cấp. Thay vì lặp lại câu trả lời, hãy bỏ phiếu trả lời hiện có. Một số hướng dẫn để viết câu trả lời tốt có thể được tìm thấy ở đây .
dferenc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.