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


当前回答

你觉得不舒服,所以去看医生。他检查了你的身体,认为你需要一些药物治疗。他开了一些药,并把处方打电话到你当地的药店。你回家吧。稍后,你的药房打电话告诉你,你的处方已经准备好了。你去捡吧。

其他回答

将方法看作是将任务交给同事。一个简单的任务可能如下:

Solve these equations:
x + 2 = y
2 * x = 3 * y

你的同事勤奋地计算了一下,并给出了以下结果:

x = -6
y = -4

但是你的同事有一个问题,他并不总是理解符号,比如^,但是他通过它们的描述理解它们。如指数。每次他找到其中一个,你就会得到以下信息:

I don't understand "^"

这就要求你在向你的同事解释完这个角色的含义后,再重写一遍你的整个指令集,而他并不总是在问题之间记得。而且他也很难记住你的建议,比如直接问我。然而,他总是尽可能地遵循你的书面指示。

你想到了一个解决方案,你只需在所有的指令中添加以下内容:

If you have any questions about symbols, call me at extension 1234 and I will tell you its name.

现在,无论何时他有问题,他都会打电话问你,而不是给你一个糟糕的回复,让整个过程重新开始。

假设你需要一个函数返回10的平方,那么你写一个函数:

function tenSquared() {return 10*10;}

之后你需要9的平方,所以你写了另一个函数:

function nineSquared() {return 9*9;}

最终你将用一个泛型函数替换所有这些:

function square(x) {return x*x;}

同样的想法也适用于回调。你有一个函数,它做一些事情,当完成时调用doA:

function computeA(){
    ...
    doA(result);
}

之后你想要完全相同的函数调用doB,而不是你可以复制整个函数:

function computeB(){
    ...
    doB(result);
}

或者你可以将回调函数作为变量传递,并且只需要使用该函数一次:

function compute(callback){
    ...
    callback(result);
}

然后你只需要调用compute(doA)和compute(doB)。

除了简化代码之外,它还让异步代码通过在完成时调用任意函数来让您知道它已经完成,这与打电话给某人并留下回调号码类似。

在PHP中,它是这样的:

<?php

function string($string, $callback) {
     $results = array(
        'upper' => strtoupper($string),
        'lower' => strtolower($string),
        );

    if(is_callable($callback)) {
        call_user_func($callback, $results);
    }
}

string('Alex', function($name) {
    echo $name['lower'];
});

现实生活中的例子

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

当我下午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非常粗鲁。

回呼是一个贴了邮票的回邮信封。当你调用一个函数时,就像发送一封信一样。如果您希望该函数调用另一个函数,则以引用或地址的形式提供该信息。