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

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

通常,我发现它在AJAX中使用。 在httpRequest.onreadystatechange 。 这与Java的multithreading类似吗? 响应的听众如何以及在哪里? 异步编程是否类似于multithreading?

在以下代码中,控制流程如何:

 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 )

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

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

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

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

这是一个很好的video,讨论更多关于浏览器中使用的事件循环,以及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(); 

不太像multithreading……

您可以在需要等待主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在数据发生变化时回调一样。 虽然不是严格的异步,但通常不会立即调用该回调。 相反,它将在稍后响应事件的某种用户操作而被调用。

因为正在执行的javascript是异步的,所以如果你在发出异步请求之后只放了任何旧函数,它可能会在原始请求完成之前被调用。 原始请求将在BEGINS(发出)后立即返回,而不是完成。

如果您需要对异步请求的结果执行某些操作,或将请求链接在一起等,则需要回调以确保在上一步完成之前不会开始下一步。

回调在JS中被广泛使用,特别是在jQuery中。

您需要使用回调的地方是语言无法协调的任何地方。 协调意味着代码在它需要的数据准备好之前不会执行。 同步呼叫是协调的; 在子计算完成之前,调用不会返回。 异步调用不会给你协调,所以你需要一个回调。

  • 事件驱动的计算可能不协调,因此事件处理程序是回调:

        =   

    特别是在JS中,事件调度的协调(例如DOM dispatchEvent ,jQuery trigger和IE fireEvent方法)未指定( 嵌套DOM事件除外,它们是同步的)。 如果触发事件,则可以延迟处理程序并立即执行返回触发后的任何内容。 事件处理程序通常是同步调用的,但它们并不需要。

  • JQuery 效果通常会在完成后执行回调。 动画函数是异步的,因此它们不会阻止脚本的其余部分。

回调对于定义某些计算的外部部分的函数也很有用,但是内部部分未定义。 这里有些例子:

  • 您可以使用回调来过滤集合:

     // get odd items $([0,1,2,3,4,5,6,7,8,9]).filter(function (i) {return this % 2;}) // or: $.grep([0,1,2,3,4,5,6,7,8,9], function (x, i) {return x % 2;}); 
  • Array.sort采用回调,因此您可以定义元素的比较方式。

     [{name: 'foo', id: 1}, {name: 'bar', id: 5}, {name: 'baz', id: 3}] .sort(function (a,b) { return a.name.localeCompare(b.name); }) 

一些jQuery的DOM操作方法(例如appendprependwrap使用回调来构建基于jQuery方法提供的上下文的元素。您可以将回调视为提供计算的内部部分或作为协调的问题:当外部计算开始时,构建新DOM元素所需的数据不可用;回调完成子计算以在数据可用时创建元素。

setTimeoutsetInterval都会在延迟后执行回调。

从版本1.5开始,jQuery提供延迟对象作为管理多个回调的方式,回调之间具有各种执行依赖性。

回调非常类似于“ 延续 ”,这基本上意味着“剩下的计算”。 不同之处在于,继续表示计算的其余部分的全部,而回调表示子计算的其余部分。 Continuations是整个编程风格的一部分,被称为“ 延续传递风格 ”(CPS)。 通过continuation,您可以创建各种有趣的控制结构。 例如,continuation可用于实现exception和协同程序 。

根据语言引擎的function(特别是需要尾调用优化 ),CPS可以提供​​更有效的方法。 一些控制结构(例如协程)需要尾调用,否则你会得到堆栈溢出*

回调允许单线程操作(Javascript是单线程)异步执行。

最明显的例子是AJAX调用,你有一个在AJAX请求完成后执行的回调。 AJAX请求可能需要一段时间,如果是正常的函数调用,则在请求加载时整个页面将被冻结(无法滚动,无法选择文本等)。

这是通过setTimeoutsetInterval实现的, setInterval将稍后调用的函数排入队列,同时允许其他代码在其间执行。 因此,当您等待AJAX​​调用完成时,允许执行其他代码(包括浏览器更新)。

由于您需要AJAX以外的示例,因此异步性质的其他常见用法是动画。 动画需要回调,因为它需要允许UI绘制。

假设您希望在5秒内向右侧设置div 100px的动画。 你的第一直觉可能是创造一个循环并在两者之间睡觉。 但Javascript中没有sleep ,即使有,也会冻结UI,因为睡眠时不会发生任何事情。

相反,你需要做一些沿着增加位置10行的方法,然后使用回调调用setTimeout 500 ms来执行下一帧。 这可能是递归完成的。

另外一个用途就是将函数作为参数传递,尽管我不确定术语“回调”是否适合该用例。 这就是你在你的例子中使用它的方式, some_function可以用作回调的各种函数重用,所以有点注入代码。

当前效果结束后执行回调函数。 这里有一个更详细的例子。

异步编程是否类似于multithreading?

是。

Javascript的异步模型提供了一种“在后台”工作的方法。

假设您有一个长期运行的计算。 对于一个简单的js演示,这可能很难想象,但想象一个长的解压缩程序,或者游戏中长时间运行的路径查找算法等等。一些数值计算需要花费一秒多的时间。

通过直接调用该函数进行计算将起作用 ,但它将在计算期间暂停浏览器UI。 异步运行意味着浏览器UI保持响应,因为计算继续在后台线程上运行。 当calc完成时,根据异步编程模式,该函数调用回调函数,通知应用层计算完成。