为什么在 JavaScript 中使用回调,它有什么优点?

新手上路,请多包涵

有人可以解释一下,为什么我们在 JavaScript 中使用回调?我找到了例子,但它们可以通过使用普通函数来实现。使用它有什么好处?我得到了“如何”使用它的答案,而不是“为什么以及何时”我们需要使用它。

通常,我发现它被用于 AJAX。在 httpRequest.onreadystatechange 上。这和Java的多线程是不是很像?响应的侦听器如何以及在哪里?异步编程类似于多线程吗?

在下面的代码中,控制流程是怎样的:

 function some_function(arg1, arg2, callback) {
  var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
  callback(my_number);
  some_different_function_not_callback(arg1);
}
some_function(5, 15, function(num) {
   console.log("callback called! " + num);
});

来自 JQuery 网站:

回调的特殊之处在于,出现在“父级”之后的函数可以在回调执行之前执行”(参考: http ://docs.jquery.com/Tutorials:How_jQuery_Works)

有人可以用一个例子向我解释这一行吗?

原文由 Harke 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 258
2 个回答

主浏览器进程是一个单线程事件循环。如果您在单线程事件循环中执行长时间运行的操作,则进程会“阻塞”。这很糟糕,因为进程在等待您的操作完成时停止处理其他事件。 ‘alert’ 是为数不多的阻塞浏览器的方法之一:如果您调用 alert(‘test’),您将无法再单击链接、执行 ajax 查询或与浏览器 UI 交互。

为了防止阻塞长时间运行的操作,XMLHttpRequest 提供了一个异步接口。您将回调传递给它以在操作完成后运行,并且在处理过程中将控制权交还给主事件循环而不是阻塞。

没有理由使用回调,除非您想将某些东西绑定到事件处理程序,或者您的操作可能会阻塞,因此需要异步编程接口。

这是一个很好的视频,更多地讨论了浏览器中使用的事件循环,以及 node.js 中服务器端的事件循环。

编辑:jQuery 文档中那条令人费解的行只是意味着回调异步执行,因为控制被交还给主事件循环。

 parent_function(function () { console.log('Callback'); });
parent_doesnt_block(); // <-- function appears after "parent"
therefore_execution_continues();
// Maybe we get 'Callback' in the console here? or maybe later...
execution_still_continues();

原文由 EMI 发布,翻译遵循 CC BY-SA 3.0 许可协议

不太像多线程…

任何时候需要等待主 JS 代码外部的东西时,都可以使用回调。在浏览器中,它大量用于 AJAX,而在 node.js 中,它用于调用系统的每一件事(文件访问、网络访问、数据库请求等)。

假设您希望在用户每次单击按钮时触发一个 ajax 请求。现在假设 ajax 请求需要 10 秒才能完成。然后,在这 10 秒结束之前,用户单击其中的 10 个按钮。这将重复调用这样的函数:

 var clicked = function() {
  doAjax('/some/path.json', function(result) {
    updatePageWith(result.widgets);
  });
};

这在 JS 引擎中运行代码的时间仅足以发出请求。然后它在等待时闲置。此时其他 JS 可以运行,UI 完全流畅且可交互,一切都很棒。然后突然间,所有 10 个请求立即解决。然后我们的回调像魔术一样被调用 10 次。

这是有效的,因为每次我们调用 clicked() 我们都会创建一个新的函数对象,并将其传递给 doAjax() 函数。所以内存中有 10 个唯一的回调函数对象,每个对象都通过 doAjax() 函数绑定到一个特定的请求。当请求返回时,它会找到关联的回调对象并调用它。

这里的巨大优势是,尽管 javascript 是单线程的,但您永远不会因为 等待 而占用该线程。如果你的 JS 线程很忙,那应该只是因为它正在积极运行代码。因此,即使 JS 是单线程的,您的代码隐式保存任意数量的任意类型的异步任务的状态也是微不足道的。

回调的 同步 方法通常用于不同的目的。像听众或代表。就像告诉对象 A 在数据更改时进行回调一样。虽然不是严格异步的,但您通常不会立即调用该回调。相反,稍后将调用它以响应某种用户事件操作。

原文由 Alex Wayne 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题