Nodejs 支持函数作为参数注册回调函数,彰显其支持异步处理特性

Nodejs 支持函数作为参数注册回调函数,彰显其支持异步处理特性
C 也同样支持函数可以作为参数注册回调,而且检查严格,node.js 根本不作检查,等运行就 crash·

有木有同感

28 回复

Diss nodejs 你至少拿 c 艹 11 的 lambda 吧…


typescript 欢迎你

倒不是 diss,node.js 新手,之前只有 C 相关经验,学习的过程中有些疑惑,所以来交流交流;
node.js 标榜异步处理,我现在的理解就是注册回调,把那些耗费时间的处理扔到回调函数里面去处理,不要阻塞 node.js 处理的单线程即可,其实工作还是在那,只不过是谁(线程)来处理的问题,node.js 很取巧的把重活累活都扔给别人了…是不是可以这样理解?

多谢大侠讨论



如果你只有 c 基础的话,那你首先要理解的在一门函数式语言里面,一个函数和一个 int 或者一个 string 是一样的东西,可以在运行时被创建,被传递。

函数作为参数不是什么新奇的操作,所有语言都能这么干,重点的一个函数能接受函数作为参数并且能返回一个全新的函数。

接下来再讲讲异步。

在 jsruntime 里面

光一个函数怎么做回调啊。还得有 context。

C 函数不支持闭包
ObjC 的一个扩展倒是可以支持闭包

在 js runtime 里面,永远都有且只有一条线程在执行 js 代码。worker 之类的情况先不讲。

所以只要是用 js 写的代码,不管你写在哪里,只要是 js,一旦阻塞,那整个程序都会阻塞。

不会阻塞的是提供的 api 不会阻塞。

c 里强行靠指针和全局变量来回调的,至少折腾 moogose 的时候着实被恶心了一次。

一般 node 里面不是为了回调而回调,而是因为涉及异步 io。

感谢🙏
1、函数在运行时被创建,这个该如何理解? c 的函数调用也是运行时创建,离开后销毁;
2、js 处理由一个线程负责,js 处理阻塞了该线程也就阻塞了,这个理解,你说的 API 不阻塞是什么意思,无法体会…
再次感谢

你既然学过 c, 可以对比一下: c 的函数本身都是静态分配的, 编译好即使不运行, 用 nm 也可看到.

相对地, 你可以考虑一下纯 c 怎么实现一个 Function.prototype.bind

  1. 别的语言当然也会用自己的方法实现异步,只是在 Node 里写异步更轻松一些
    2. 动态语言、鸭子语言当然是在运行时才“检查”的(也不是检查,就是实在运行不了 crash ),想更严格地提前检查就用别的办法(办法有很多)。这有坏处也有好处,世事没有完美。

node 这个所谓的回调学名叫 CPS

异步( asynchronous )可以简单理解为领导安排下去一个事情,然后就不用管了,等到下面干完了再通知回来
典型栗子是 OS 的中断机制,也可以说基于中断的 IO 本身就是异步的
至于处理是否“耗费时间”,还有线程什么的,站在异步的角度都属于 implementation detail,异步就是指上面那个机制

非阻塞( non-blocking )直观理解就是 POSIX API 里面的 fcntl(fd, F_SETFL, O_NONBLOCK),区别是 read 和 write 调用立即返回,但不一定成功(返回值会 indicate 这个状态),放到 node 里面就是 fs.readFile 这种在它自己的函数里面是立即返回的
这个东西玩过 nginx 之类的应该明白

这些 term 的区别是很微妙的

区别在于 c 的函数是在编译期间就生成的,而 js 的函数在运行时被执行到定义语句的时候才被创建,而且类似于 js/python 这类语言的函数本质上都是一个对象,所以可以在一个函数中动态创建一个新的函数作为返回值

支持楼主转 java

感觉题主没明白 node 异步的本质

js 核心主要分两部分:v8 和 libuv,v8 用来解释 js 代码本身

libuv 用来支持异步操作,例如创建定时器,发送网络请求等等

libuv 实际上是用 c 写的事件循环框架,支持 c 以回调的方式运行。所以通过 libuv 结合 v8 写出一下常用操作的 binding (定时器,网络,文件等),node 就支持了异步。

关于 libuv,可以参考 http://docs.libuv.org/en/v1.x/

在 NodeJS 中不阻塞的 API 通常由原生方法,或者基于原生方法构建的 API,最简单的例子就是 fs.readFile,或者是 HTTP 相关的 API,在 node 层来看是不阻塞的,通过回调函数来告知过程结束。nodejs 下面还有一个 libuv,libuv 会创建线程的,但是这一层不是由 js 来负责。

对于 LZ 这种声称"只有 C 相关经验"的人来说 call/cc 真是一种降维打击.API 不同步有什么难理解的?凭什么所有语言的运行模型都必须像 C 那样只能沿着子程序调用链走.更何况 LZ 连 C 都没搞明白.“函数调用运行时创建,离开后销毁”.麻烦 LZ 能不能先解释解释"函数调用的创建和销毁"是怎么一回事.

这个……我不是来说 nodejs 不好的,学习了 nodejs 几天,一些思路与想法与大伙交流而已;至于函数创建和销毁的问题,因为只有 C 经历,我的理解是就是函数相关堆栈的申请释放,C 中的函数是执行过程的集合;不知道你所说的 nodejs 中的函数创建 /销毁是什么概念

大佬息怒,wwqgtxx 同学给我解释了 nodejs 的函数创建与释放了~

C 函数因为是编译期间就确定好的,执行的过程中也就涉及到堆栈的申请 /销毁; JS 可以在运行的过程中动态的创建 /销毁函数,得益于 NODEJS 所有函数属于同一个对象,对吧?貌似理由不是很充分。。。
多谢

大概明白了非阻塞 API 的概念了,你说的 native api 其实就是 nodejs 提供的一些底层的 api,非阻塞其实就是调用不阻塞,执行是否阻塞(是否同步返回执行结果)要看具体功能了,这样理解对吧?

一个新问题,如果一个 http 请求触发一个非阻塞的耗时的 api 调用,在 http 请求超时之前都没能完成该原生 api 的执行,等到其执行完成后,回调函数被调用执行,但是 HTTP 请求已经超时,这中非阻塞还有意义吗?就是在阻塞的时间未知的情况下,要在规定的时间段内完成任务,这种矛盾如何解决~是我等菜鸟想的太多吗?

感谢

你可以把 js 中的每个函数都理解成 c++中的一个类的实例,每一个 function 都是运行时 new 出来的
至于你说的超时问题,所有异步 api 一般都会提供 timeout 设置,超时的时候会调用特殊的超时回调函数或者给普通回调函数传一个错误信息标识,另外异步操作也是可以被打断的呀,你在主线程设置一个定时器,超时了直接中断那个异步操作不就得了

感谢,在 NODEJS 架构中,如果 JS 调用阻塞的 native api,譬如文件读写,是不是常规做法是启动新的线程来做文件读写的工作?

在写 node 的时候,这不是你该考虑的问题,就比如你写 c 的时候不需要考虑 fwrite 是如何调用系统内核的 sys_write 函数,内核又是怎么寻道然后把数据通过 dma 发给硬盘,硬盘又是怎么写入到磁粉上的

node 的世界中最大特点是不提供任何阻塞 api,记住 node 永远是单线程的,至于它底层是怎么实现的,不是你该关心的

还以为 node 有什么新特性了呢? 现在讨论这个? 大清亡了?

不好意思惊扰大神,菜鸟在学习中

在Node.js中,函数作为参数注册回调函数是其支持异步处理的核心特性之一。这种设计使得Node.js能够高效地处理I/O密集型任务,比如网络请求、文件读写等。下面是一个简单的示例,展示了如何使用Node.js的回调函数来处理异步操作。

// 定义一个模拟异步操作的函数,比如读取文件
function readFileAsync(filePath, callback) {
    // 使用setTimeout模拟异步延迟
    setTimeout(() => {
        try {
            // 假设文件内容已经读取到
            const content = "Hello, this is the file content!";
            callback(null, content); // 调用回调函数,传递null作为错误参数,表示成功
        } catch (error) {
            callback(error); // 调用回调函数,传递错误参数
        }
    }, 1000); // 延迟1秒
}

// 注册回调函数
readFileAsync("example.txt", (error, content) => {
    if (error) {
        console.error("Error reading file:", error);
    } else {
        console.log("File content:", content);
    }
});

在这个示例中,readFileAsync函数接受一个文件路径和一个回调函数作为参数。它使用setTimeout来模拟异步操作,并在1秒后调用回调函数。回调函数接受两个参数:一个用于错误处理,另一个用于成功时的数据传递。

这种设计模式允许Node.js在不阻塞主线程的情况下处理异步任务,从而提高了应用程序的响应性和性能。随着ES6引入的Promiseasync/await语法,Node.js的异步编程变得更加简洁和直观,但回调函数仍然是理解和使用Node.js异步特性的基础。

回到顶部