一句话回答:异步性。
问题的答案让我们首先跟踪常见行为。在所有示例中,
outerScopeVar都在函数内部修改了。该函数显然不会立即执行,而是被分配或作为参数传递。这就是我们所说的回调。
现在的问题是,何时调用该回调?
这要视情况而定。让我们尝试再次跟踪一些常见行为:
img.onload
可能会在将来的某个时间(以及(如果))成功加载图片时调用。setTimeout
在延迟到期并且超时未被取消后,可能在将来的某个时间调用clearTimeout。注意:即使0用作延迟,所有浏览器也具有最小超时延迟上限(在HTML5规范中指定为4毫秒)。- jQuery
$.post
的回调可能在将来的某个时间(如果(如果)成功完成Ajax请求)调用。 - 当文件已成功读取或引发错误时,将来
fs.readFile
可能会调用Node.js。
在所有情况下,我们都有一个回调,它可能在将来的某个时间运行。这种“将来的某个时候”就是我们所说的异步流。
异步执行从同步流中推出。也就是说,异步代码将永远不会在同步代码堆栈正在执行时执行。这就是Javascript是单线程的意思。
更具体地说,当JS引擎处于空闲状态时-不执行(a)同步代码的堆栈-它将轮询可能触发异步回调的事件(例如,过期的超时,收到的网络响应),然后一个接一个地执行它们。这被视为事件循环。
也就是说,以手绘红色形状突出显示的异步代码只有在执行了它们各自代码块中的所有其余同步代码之后,才可以执行:
异步代码突出显示
简而言之,回调函数是同步创建的,但异步执行。在知道异步函数已执行之前,你就不能依赖它的执行,以及如何执行?
真的很简单。应从该异步函数内部启动/调用依赖于异步函数执行的逻辑。例如,将alerts和console.logs 移到回调函数中也将输出预期的结果,因为此时该结果可用。
实现自己的回调逻辑通常,你需要根据异步函数的结果执行更多 *** 作,或者根据调用异步函数的位置对结果执行不同的 *** 作。让我们处理一个更复杂的示例:
var outerScopeVar;helloCatAsync();alert(outerScopeVar);function helloCatAsync() { setTimeout(function() { outerScopeVar = 'Nya'; }, Math.random() * 2000);}
注意:我使用
setTimeout随机延迟作为通用异步函数,同一示例适用于
Ajax readFile,
onload和任何其他异步流。
该示例显然遭受与其他示例相同的问题,它不等待异步函数执行。
让我们解决实现自己的回调系统的问题。首先,我们摆脱了
outerScopeVar在这种情况下完全没有用的丑陋之处。然后,我们添加一个接受函数参数的参数,即回调。当异步 *** 作完成时,我们调用此回调传递结果。实现(请按顺序阅读注释):
// 1. Call helloCatAsync passing a callback function,// which will be called receiving the result from the async operationhelloCatAsync(function(result) { // 5. Received the result from the async function, // now do whatever you want with it: alert(result);});// 2. The "callback" parameter is a reference to the function which// was passed as argument from the helloCatAsync callfunction helloCatAsync(callback) { // 3. Start async operation: setTimeout(function() { // 4. Finished async operation, // call the callback passing the result as argument callback('Nya'); }, Math.random() * 2000);}
上面示例的代码片段:
// 1. Call helloCatAsync passing a callback function,// which will be called receiving the result from the async operationconsole.log("1. function called...")helloCatAsync(function(result) { // 5. Received the result from the async function, // now do whatever you want with it: console.log("5. result is: ", result);});// 2. The "callback" parameter is a reference to the function which// was passed as argument from the helloCatAsync callfunction helloCatAsync(callback) { console.log("2. callback here is the function passed as argument above...") // 3. Start async operation: setTimeout(function() { console.log("3. start async operation...") console.log("4. finished async operation, calling the callback, passing the result...") // 4. Finished async operation, // call the callback passing the result as argument callback('Nya'); }, Math.random() * 2000);}
在实际使用案例中,大多数情况下,DOM API和大多数库已经提供了回调功能(
helloCatAsync此示例中的实现)。你只需要传递回调函数,并了解它将在同步流之外执行,并重新组织代码以适应该情况。
你还将注意到,由于异步特性,不可能将return值从异步流返回到定义了回调的同步流,因为异步回调在同步代码已经完成执行很长时间之后才执行。
而不是
return从异步回调中获取值,你将不得不使用回调模式,或者。。。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)