一尘不染

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

javascript

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

让我们考虑一下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代码段。我通常在问-
是什么使程序在遇到回调函数时不断执行的技巧。使回调概念成为非阻塞性语法的语法是什么?

提前致谢!


阅读 654

收藏
2020-04-25

共1个答案

一尘不染

没有 在告诉你,你的回调是异步执行的语法。回调可以是异步的,例如:

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);

异步代码如何工作

Java本身不具有使函数异步的任何功能。如果要编写异步函数,则有两个选择:

  1. 使用另一个异步函数(例如setTimeout或Web Worker)来执行您的逻辑。

  2. 用C写

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

事件循环

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

在浏览器的中心,有一个从异步网络代码演变而来的事件循环。因此,它使用I /
O原语作为其核心就不足为奇了:select()或类似OS之类的类似民意调查,epoll等)。

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

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

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

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

方便地,select()还允许您指定一个timeout值。通过仔细管理timeout传递给select()您的内容,可以导致将来某个时候调用回调。这是怎么setTimeoutsetInterval实现。解释器会保留所有超时的列表,并计算需要传递的timeout时间select()。然后,当select()返回时,除了要确定是否由于I
/ O操作需要调用任何回调外,解释器还会检查是否需要调用任何过期的超时。

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

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

2020-04-25