Open SunShinewyf opened 6 years ago
感觉博主说的console这块有点问题啊,console是基于process.stdout.write,但是console.log(a),a这个参数是直接传进来的:代码链接。
这这里是对args的处理,再看write:代码链接,在调用process.stdout.write之前已经拼接好string了,所以应该是console.log的args是同步的,只不过在输出的时候调用的process.stdout.write。
而write的异步问题,确实代码里面进行了兼容处理了:代码链接,通过event emitter的once方法对error进行监听,这样就相当于把write丢到了libuv的event loop中了,无论同步异步,只要触发了error,就会打断,而下面的catch,则是怕运行栈溢出,直接hold住uv_defalut_loop了,所以会catch一下,做下溢出判断。
但是wirte里面只是调用了stream.write(string, errorhandler);而且try里面只是捕获同步错误,对于你上面说的不论同步还是异步,都会走进catch有点不太理解,而且这个容错处理和stream.write是同步还是异步有什么关系呢
异步不会走到catch,我这里说的确实有问题。
你关联一下上下文,stream.write其实是process.stdout.write,而process.stdout官网api文档介绍了是一个socket流,因此会继承于event emitter,所以
stream.once('error', noop);
这个方法是存在的,而error事件是error handler,专门用来监听error的。
如果process.stdout是异步的,那么可以直接监听到process.stderr,然后通过
stream.write(string, errorhandler);
errorhandler来返回错误信息。
如果process.stdout是同步的且在执行的时候存在运行栈溢出的情况,这种时候通过监听'error'事件可以判断是否是运行栈溢出,因为stream.once
是event emitter,所以回调noop会等到主loop执行完后执行。如果运行栈溢出,noop无法被触发,会抛错并走到catch流程中,而在catch中,如果是第一次运行栈溢出,会通过try来拿到运行栈溢出错误的message:
if (MAX_STACK_MESSAGE === undefined) {
try {
// eslint-disable-next-line no-unused-vars
function a() { a(); }
} catch (err) {
MAX_STACK_MESSAGE = err.message;
}
}
之后,通过对比第一次try的e.message和运行栈溢出的错误信息来抛出错误:
if (e.message === MAX_STACK_MESSAGE && e.name === 'RangeError')
throw e;
你说的对于write函数中对于错误处理这块我是很认同的,但对于错误捕获的部分和process.stdout.write是异步还是同步好像没有什么很直接的关联吧,还是说我没有完全get到你想要表达“我阐述的console这块有问题的点”....
……
但是,这段代码运行的时候,浏览器可能会认为需要把控制台I/O 延迟到后台,在这种情况下,等到浏览器控制台输出对象内容时,a.index++ 可能已经执行,因此会显示{ index: 2 }。
你说的这句话,应该是不会发生的,因为在执行这个函数console.log(a)
的时候,a就已经固定了,引用之前我说过的那句话:
在调用process.stdout.write之前已经拼接好string了
只不过在输出到控制台的时候可能会在a.index++后面,因为输出是用的process.stdout.write。
上面评论中你引用的那个描述:
但是,这段代码运行的时候,浏览器可能会认为需要把控制台I/O 延迟到后台,在这种情况下,等到浏览器控制台输出对象内容时,a.index++ 可能已经执行,因此会显示{ index: 2 }。
是针对js中的console.log来说的,并不是针对node中的console.log,只有node中的console.log才是用的process.stdout.write来进行输出
额,好吧,我读下来感觉你在说node。😓但是我在浏览器中也没遇到过这种情况。不过process.stdin/out底层调用的都是libuv,而chrome底层是开发人员二次开发的libuv2,理论上chrome的表现应该和node差不多,不过我也不十分确定。。。
@xtx1130 chrome中console.log的解释是直接摘自《你不知道的JavaScript》的。不过很感谢和你交流那么多,感觉你研究得很深~
@SunShinewyf 哦哦,YDK js 那本书是么,我还真没仔细看过。。。有时间摘出来翻翻。不过chrome源码确实没看过。
@xtx1130 链接 建议看看,还不错~
应该是一样的,只是译本吧,哈哈
对于
process
部分的API,这里不详细讲,具体可移步这里node 中的 console.log
js
中的console.log
和node
中的console.log
还是有一些区别的。js
中的console.log
在一些情况下执行时存在异步情况,根据《你不知道的JavaScript中卷》中的描述:可见,
js
中的console.log
并不是严格同步或者异步,而是取决于执行环境和I/O之间的异步化。在
node
中,console.log
的底层实现代码如下:底层使用的是
process.stdout.write
。node
中的console.log
是同步还是异步的呢,也就是process.stdout.write
是同步的还是异步的。官方文档给出的解释是:process 的 child_process
child_process
是node
中一个比较重要的模块,众所周知,node有一个一直被人诟病的地方就是“单进程单线程”,但是有了child_process
之后,node就可以实现在程序中直接创建子进程,除此之外,子进程和主进程之间还可以进行通信,这样就榨干了cpu的资源,使资源得到了充分地利用。如何创建一个子进程,创建同步进程:
创建异步进程:
各个不同的方法之间的关系如下:
exec、execFile、fork
都是通过spawn
封装而成,由此可见,spawn
是最基础的,它只能运行指定的程序,参数需要在列表中列出,但是exec
在执行时则衍生出一个shell并在shell上运行。和exec
类似的是execFile
,但它执行命令,无需衍生出一个shell,所以execFile
比exec
更加安全,也更高效。fork
也是在spawn
中封装出来的,专门用于衍生新的Node.js进程,跟 child_process.spawn() 一样返回一个 ChildProcess 对象。 返回的 ChildProcess 会有一个额外的内置的通信通道,它允许消息在父进程和子进程之间来回传递。详细的可以参见官方文档
孤儿进程和僵尸进程
在unix/linux中,子进程是父进程创建的,子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。
node中 的 cluster
cluster
是node
内置的一个模块,用于node多核处理,它的工作进程由child_process.fork()
方法创建,因此它们可以使用IPC和父进程通信,从而使各进程交替处理连接服务。cluster
中创建worker
的源码如下:而且手动传了一些环境变量的参数值。如下是根据
cluster.isMaster
标识来fork
子进程的代码:cluster
模块支持两种连接分发模式(将新连接安排给某一工作进程处理)。第一种方法(也是除
Windows
外所有平台的默认方法),是循环法。由主进程负责监听端口,接收新连接后再将连接循环分发给工作进程。在分发中使用了一些内置技巧防止工作进程任务过载。第二种方法是,主进程创建监听socket后发送给感兴趣的工作进程,由工作进程负责直接接收连接。
理论上第二种方法应该是效率最佳的,但在实际情况下,由于操作系统调度机制的难以捉摸,会使分发变得不稳定。我们遇到过这种情况:8个进程中的2个,分担了70%的负载。
总结
这里只是集合一些知识点,结合饿了么前端面试整理了一些自己对这一块不太清楚的知识点,权当笔记用了,以后可以方便回顾~
参考文档: