我知道回调函数是异步运行的,但为什么呢?

时间:2015-04-26 21:05:43

标签: javascript node.js asynchronous callback

语法的哪一部分提供了此函数应该在其他线程中运行并且是非阻塞的信息?

让我们考虑node.js中的简单异步I / O

 var fs = require('fs');
 var path = process.argv[2];

  fs.readFile(path, 'utf8', function(err,data) {
   var lines = data.split('\n');
   console.log(lines.length-1);
  });

究竟是什么导致它在后台发生?任何人都可以准确地解释它或粘贴一些好资源的链接?我看到的每个地方都有很多关于回调是什么的信息,但没有人解释为什么它实际上是这样的。

这不是关于node.js的具体问题,而是关于每种编程语言中回调的一般概念。

编辑:

我提供的例子可能不是最好的。所以我们不要考虑这个node.js代码片段。我一般都在问 - 当遇到回调函数时,程序是如何继续执行的。什么是语法 这使得回调概念成为非阻塞概念吗?

提前致谢!

3 个答案:

答案 0 :(得分:19)

语法中有 nothing ,告诉您回调是异步执行的。回调可以是异步的,例如:

setTimeout(function(){
    console.log("this is async");
}, 100);

或它可以是同步的,例如:

an_array.forEach(function(x){
    console.log("this is sync");
});

那么,你怎么知道一个函数是同步还是异步调用回调?唯一可靠的方法是阅读文档。

您还可以编写测试以查明文档是否不可用:

var t = "this is async";
some_function(function(){
    t = "this is sync";
});

console.log(t);

异步代码如何工作

Javascript本身并没有使函数异步的任何功能。如果要编写异步函数,可以有两个选项:

  1. 使用其他异步函数(如setTimeout或web worker)来执行逻辑。

  2. 将其写在C中。

  3. 关于C编码函数(如setTimeout)如何实现异步执行?这一切都与事件循环(或大部分)有关。

    事件循环

    在Web浏览器中,有一段用于联网的代码。最初,网络代码只能下载一件事:HTML页面本身。当Mosiac发明了<img>标签时,网络代码逐渐演变为下载多个资源。然后Netscape实现了progressive rendering个图像,他们必须使网络代码异步,以便他们可以在加载所有图像之前绘制页面并逐步和单独更新每个图像。这是事件循环的起源。

    在浏览器的核心,有一个从异步网络代码演变而来的事件循环。因此,它使用I / O原语作为其核心并不奇怪:select()(或类似的东西,如poll,epoll等取决于操作系统)。

    C中的select()函数允许您在单个线程中等待多个I / O操作,而无需生成其他线程。 select()看起来像:

    select (max, readlist, writelist, errlist, timeout)
    

    要让它等待I / O(来自套接字或磁盘),您需要将文件描述符添加到readlist,并且当您的任何I /上有可用数据时它将返回O频道。一旦它返回,您可以继续处理数据。

    javascript解释器保存您的回调,然后调用select()函数。当select()返回时,解释器会确定哪个回调与哪个I / O通道关联,然后调用它。

    方便地,select()还允许您指定timeout值。通过仔细管理传递给timeout的{​​{1}},您可以在将来的某个时间调用回调。这就是select()setTimeout的实施方式。解释器会保留所有超时的列表,并计算出setIntervaltimeout需要传递的内容。然后当select()返回时,除了发现是否存在由于I / O操作而需要调用的任何回调之外,解释器还会检查需要调用的任何过期超时。

    因此select()仅涵盖了实现异步函数所需的几乎所有功能。但现代浏览器也有网络工作者。对于Web worker,浏览器会生成线程以异步方式执行javascript代码。要与主线程进行通信,工作人员仍必须与事件循环(select()函数)进行交互。

    Node.js在处理文件/磁盘I / O时也会生成线程。当I / O操作完成时,它会与主事件循环进行通信,以使相应的回调执行。

    希望这能回答你的问题。我一直想写这个答案但是之前很忙。如果您想了解更多有关C中的非阻塞I / O编程的建议,我建议您阅读:http://www.gnu.org/software/libc/manual/html_node/Waiting-for-I_002fO.html

答案 1 :(得分:0)

回调不一定是异步的。执行完全取决于fs.readFile如何决定处理函数参数。

在JavaScript中,您可以使用例如setTimeout异步执行函数。

讨论和资源:

How does node.js implement non-blocking I/O?

Concurrency model and Event Loop

Wikipedia

  

有两种类型的回调,它们在运行时控制数据流的方式不同:阻止回调(也称为同步回调或只是回调)和延迟回调(也称为异步回调)。

答案 2 :(得分:-1)

首先,如果某些内容不是Async,则表示它已被阻止。因此,javascript运行器会在该行停止,直到该函数结束(这就是readFileSync将执行的操作)。

众所周知,fs是一个IO库,因此需要时间(告诉硬件读取某些文件不是立即完成的事情),所以任何不需要的东西都很有意义只有CPU,它是异步的,因为它需要时间,并且不需要冻结剩下的代码来等待另一块硬件(当CPU空闲时)。

我希望这能解决你的疑虑。