如何用简单的英语解释回调?它们与从一个函数调用另一个函数从调用函数获取上下文有什么不同?如何向新手程序员解释它们的强大功能?


当前回答

现实生活中的例子

这里有一个真实的例子,嗯,我自己的生活。

当我下午5点结束我的工作时,我的待办事项清单上有很多事情:

打电话给兽医要我的狗的检查结果。 遛狗。 处理我的税务问题。 洗碗。 回复私人邮件。 洗衣服。

当我打电话给兽医时,我接到了一个接待员的电话。接待员告诉我,我需要等待兽医,这样兽医就可以向我解释测试结果。接待员想让我等一下,直到兽医准备好。

你对此有什么反应?我知道我的工作多么低效!所以我向接待员提议,当兽医准备好谈话时,他让兽医给我回个电话。这样,我就不用等电话了,我可以做其他的事情。等兽医准备好了,我就可以把其他的事情暂时搁置,和她谈谈。

它和软件有什么关系

我是单螺纹的。我一次只能做一件事。如果我是多线程的,我将能够并行处理多个任务,但不幸的是,我不能这样做。

如果回调不是一个东西,当我遇到异步任务时,它会阻塞。如。当我打电话给兽医时,兽医需要大约15分钟来完成她正在做的事情,然后她才能和我说话。如果没有回调,我在这15分钟内就会被屏蔽。我就只能坐着等,而不能做其他的工作。

下面是没有回调的代码的样子:

function main() {
  callVet();
  // blocked for 15 minutes
  walkDog();
  doTaxes();
  doDishes();
  answerPeronalEmails();
  doLaundry();
}

现在用回调:

function main() {
  callVet(function vetCallback(vetOnThePhoneReadyToSpeakWithMe) {
    talkToVetAboutTestResults(vetOnThePhoneReadyToSpeakWithMe);
  });
  walkDog();
  doTaxes();
  doDishes();
  answerPeronalEmails();
  doLaundry();
}

更一般地说,当您处于单线程执行环境中,并且有某种异步任务时,您可以使用回调以更合乎逻辑的顺序执行事情,而不是让该任务阻塞您的单线程。

一个很好的例子是,如果您有一些前端代码需要发出ajax请求。如。如果您有一个显示用户信息的仪表板。下面是它如何在没有回调的情况下工作。用户将立即看到导航栏,但他们必须等待一段时间才能看到边栏和页脚,因为ajax请求getUser需要一段时间(作为经验法则,网络被认为是很慢的)。

function main() {
  displayNavbar();
  const user = getUser();
  // wait a few seconds for response...
  displayUserDashboard(user);
  displaySidebar();
  displayFooter();
}

现在用回调:

function main() {
  displayNavbar();
  getUser(function (user) {
    displayUserDashboard(user);
  });
  displaySidebar();
  displayFooter();
}

通过利用回调,我们现在可以在ajax请求的响应返回给我们之前显示边栏和页脚。这就好比我对接待员说:“我不想在电话上等15分钟。兽医准备好和我谈谈的时候给我回电话,与此同时,我会继续做我待办事项清单上的其他事情。”在现实生活中,你可能应该更优雅一些,但在编写软件时,你可以对CPU非常粗鲁。

其他回答

如何用简单的英语解释回调?

简单地说,回调函数就像一个Worker,当他完成一个任务时,他就会“回调”给他的Manager。

它们与从一个函数调用另一个函数有什么不同 从调用函数中获取一些上下文?

的确,您正在从另一个函数调用一个函数,但关键是回调被视为对象,因此您可以根据系统的状态(如策略设计模式)更改要调用的函数。

如何向新手程序员解释它们的强大功能?

在需要从服务器获取数据的ajax风格网站中,可以很容易地看到回调的强大功能。下载新数据可能需要一些时间。如果没有回调,在下载新数据时,整个用户界面将“冻结”,或者您将需要刷新整个页面而不仅仅是其中的一部分。使用回调函数,您可以插入“现在正在加载”的图像,并在加载后用新数据替换它。

一些没有回调的代码:

function grabAndFreeze() {
    showNowLoading(true);
    var jsondata = getData('http://yourserver.com/data/messages.json');
    /* User Interface 'freezes' while getting data */
    processData(jsondata);
    showNowLoading(false);
    do_other_stuff(); // not called until data fully downloaded
}

function processData(jsondata) { // do something with the data
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

回调函数:

下面是一个回调的例子,使用jQuery的getJSON:

function processDataCB(jsondata) { // callback: update UI with results
   showNowLoading(false);
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

function grabAndGo() { // and don't freeze
    showNowLoading(true);
    $('#results_messages').html(now_loading_image);
    $.getJSON("http://yourserver.com/data/messages.json", processDataCB);
    /* Call processDataCB when data is downloaded, no frozen User Interface! */
    do_other_stuff(); // called immediately
}

关闭:

通常,回调需要使用闭包从调用函数访问状态,这就像Worker在完成任务之前需要从Manager获取信息一样。要创建闭包,你可以内联函数,这样它就可以看到调用上下文中的数据:

/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) { 
    if (null == dtable) { dtable = "messages"; }
    var uiElem = "_" + dtable;
    showNowLoading(true, dtable);
    $('#results' + uiElem).html(now_loading_image);
    $.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
       // Using a closure: can "see" dtable argument and uiElem variables above.
       var count = jsondata.results ? jsondata.results.length : 0, 
           counterMsg = ['Fetched', count, 'new', dtable].join(' '),
           // no new chatters/messages/etc
           defaultResultsMsg = ['(no new ', dtable, ')'].join(''); 
       showNowLoading(false, dtable);
       $('#counter' + uiElem).text(counterMsg);
       $('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
    });
    /* User Interface calls cb when data is downloaded */

    do_other_stuff(); // called immediately
}

用法:

// update results_chatters when chatters.json data is downloaded:
grab("chatters"); 
// update results_messages when messages.json data is downloaded
grab("messages"); 
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback); 

关闭

最后,这里是Douglas Crockford对闭包的定义:

函数可以在其他函数内部定义。内部函数可以访问外部函数的变量和参数。如果对内部函数的引用仍然存在(例如,作为回调函数),则外部函数的变量也仍然存在。

参见:

http://javascript.crockford.com/survey.html http://api.jquery.com/jQuery.when/ http://api.jquery.com/jQuery.getJSON/ http://github.com/josher19/jQuery-Parse

回调是在满足条件时被计划执行的方法。

一个“真实世界”的例子是当地的电子游戏商店。你在等待《半条命3》。你不必每天去商店查看游戏是否上架,而是在列表中注册电子邮件,以便在游戏上架时收到通知。这封邮件变成了你的“回调”,你需要满足的条件是游戏的可用性。

“程序员”的例子是一个网页,你想在点击按钮时执行一个操作。为按钮注册回调方法,然后继续执行其他任务。当/如果用户点击按钮,浏览器将查看该事件的回调列表并调用您的方法。

回调是一种异步处理事件的方法。你永远不知道回调什么时候会被执行,或者它是否会被执行。这样做的好处是可以释放程序和CPU周期,以便在等待应答时执行其他任务。

现实生活中的例子

这里有一个真实的例子,嗯,我自己的生活。

当我下午5点结束我的工作时,我的待办事项清单上有很多事情:

打电话给兽医要我的狗的检查结果。 遛狗。 处理我的税务问题。 洗碗。 回复私人邮件。 洗衣服。

当我打电话给兽医时,我接到了一个接待员的电话。接待员告诉我,我需要等待兽医,这样兽医就可以向我解释测试结果。接待员想让我等一下,直到兽医准备好。

你对此有什么反应?我知道我的工作多么低效!所以我向接待员提议,当兽医准备好谈话时,他让兽医给我回个电话。这样,我就不用等电话了,我可以做其他的事情。等兽医准备好了,我就可以把其他的事情暂时搁置,和她谈谈。

它和软件有什么关系

我是单螺纹的。我一次只能做一件事。如果我是多线程的,我将能够并行处理多个任务,但不幸的是,我不能这样做。

如果回调不是一个东西,当我遇到异步任务时,它会阻塞。如。当我打电话给兽医时,兽医需要大约15分钟来完成她正在做的事情,然后她才能和我说话。如果没有回调,我在这15分钟内就会被屏蔽。我就只能坐着等,而不能做其他的工作。

下面是没有回调的代码的样子:

function main() {
  callVet();
  // blocked for 15 minutes
  walkDog();
  doTaxes();
  doDishes();
  answerPeronalEmails();
  doLaundry();
}

现在用回调:

function main() {
  callVet(function vetCallback(vetOnThePhoneReadyToSpeakWithMe) {
    talkToVetAboutTestResults(vetOnThePhoneReadyToSpeakWithMe);
  });
  walkDog();
  doTaxes();
  doDishes();
  answerPeronalEmails();
  doLaundry();
}

更一般地说,当您处于单线程执行环境中,并且有某种异步任务时,您可以使用回调以更合乎逻辑的顺序执行事情,而不是让该任务阻塞您的单线程。

一个很好的例子是,如果您有一些前端代码需要发出ajax请求。如。如果您有一个显示用户信息的仪表板。下面是它如何在没有回调的情况下工作。用户将立即看到导航栏,但他们必须等待一段时间才能看到边栏和页脚,因为ajax请求getUser需要一段时间(作为经验法则,网络被认为是很慢的)。

function main() {
  displayNavbar();
  const user = getUser();
  // wait a few seconds for response...
  displayUserDashboard(user);
  displaySidebar();
  displayFooter();
}

现在用回调:

function main() {
  displayNavbar();
  getUser(function (user) {
    displayUserDashboard(user);
  });
  displaySidebar();
  displayFooter();
}

通过利用回调,我们现在可以在ajax请求的响应返回给我们之前显示边栏和页脚。这就好比我对接待员说:“我不想在电话上等15分钟。兽医准备好和我谈谈的时候给我回电话,与此同时,我会继续做我待办事项清单上的其他事情。”在现实生活中,你可能应该更优雅一些,但在编写软件时,你可以对CPU非常粗鲁。

通常,我们将变量发送给函数:function1(var1, var2)。

假设,你想在它被作为参数给出之前处理它:

这是一种回调类型,其中function2执行一些代码并将变量返回给初始函数。

编辑:回调这个词最常见的意思是一个函数作为参数传递给另一个函数,并在稍后的时间点被调用。这些思想存在于允许高阶函数的语言中,即将函数视为一等公民,通常用于异步编程中。onready dosomething()。在这里,只有当它准备好了,事情才会发生。

说白了,回调就是承诺。乔、简、大卫和萨曼莎拼车去上班。乔今天开车。简,大卫和萨曼莎有几个选择:

每五分钟查看一下窗户,看看乔是否出去了 继续做他们的事,直到乔按门铃。

选项1:这更像是一个轮询示例,Jane将被困在一个“循环”中,检查Joe是否在室外。在此期间简不能做其他事情。

选项2:这是回调示例。简告诉乔当他在外面时按她的门铃。她给了他一个按门铃的“功能”。Joe不需要知道门铃是如何工作的,也不需要知道它在哪里,他只需要调用那个函数,即当他在那里时按门铃。

回调是由“事件”驱动的。在这个例子中,“事件”是乔的到来。例如,在Ajax中,事件可以是异步请求的“成功”或“失败”,并且每个事件都可以有相同或不同的回调。

In terms of JavaScript applications and callbacks. We also need to understand "closures" and application context. What "this" refers to can easily confuse JavaScript developers. In this example within each person's "ring_the_door_bell()" method/callback there might be some other methods that each person need to do based on their morning routine ex. "turn_off_the_tv()". We would want "this" to refer to the "Jane" object or the "David" object so that each can setup whatever else they need done before Joe picks them up. This is where setting up the callback with Joe requires parodying the method so that "this" refers to the right object.

希望有帮助!