
简述以下代码的输出结果,并解释执行过程:
import { once, EventEmitter } from 'node:events'; import process from 'node:process'; const ee = new EventEmitter(); process.nextTick(() => { ee.emit('myevent', 42); }); const [value] = await once(ee, 'myevent'); console.log(value); const err = new Error('kaboom'); process.nextTick(() => { ee.emit('error', err); }); try { await once(ee, 'myevent'); } catch (err) { console.error('error happened', err); } 以上代码来自这里
里面涉及到的基础知识点有:
Promiseasync/awaitnexttick 队列EventEmitter最后,这真的不算八股,AI 当然可以解释清楚,但这么一段简单清晰的代码,你还不知所以然的话,那对着 Vibe Coding 出来的屎山,最后只能束手无策了。
1 Zhuzhuchenyan 3 天前 哈哈,这不是几年前我校招的时候面试官最“喜欢”的面试题嘛 看我随手出一道,console.log 1234 才是味道最足的 console.log(1); setTimeout(() => console.log(2), 0); process.nextTick(() => { console.log(3); queueMicrotask(() => console.log(4)); }); queueMicrotask(() => { console.log(5); process.nextTick(() => console.log(6)); }); new Promise(resolve => { console.log(7); resolve(); console.log(8); }).then(() => { console.log(9); process.nextTick(() => console.log(10)); queueMicrotask(() => console.log(11)); }); new Promise(resolve => { console.log(12); setTimeout(() => { console.log(13); resolve(); }, 0); }).then(() => { console.log(14); }); (async () => { console.log(15); await void 0; console.log(16); process.nextTick(() => console.log(17)); queueMicrotask(() => console.log(18)); })(); console.log(19); |
2 craftsmanship 3 天前 via Android |
3 lqm 3 天前 AI 既然清清楚楚,那怎么会堆出类似错误的屎山 |
4 |
5 4seasons 3 天前 @Zhuzhuchenyan 这个玩意儿我寻思着真的有人能分析出来吗? |
6 lanced 3 天前 @Zhuzhuchenyan 太经典了 |
7 wuxilaoshiren 3 天前 典典典 前些年面试就喜欢问这个 但是随着 AI 的到来,感觉看这些都没意义了 |
8 ssssiiiirren 3 天前 哥们你这题目出的有问题,你这程序但凡要能运行的下去,就必须先打印 42 ,然后后面就能推出来了。 如果不先打印 42 ,await 那里就卡住了。 |
9 chenluo0429 3 天前 via Android 我面试的时候从来不会问这些。一个合格的开发应当知道一些会引发典型的回调/异步等时序难以确定的场景,然后在正常的开发过程中,别这么用他们 |
10 Ketteiron 3 天前 我说个暴论,凡是用得上 EventEmitter 的项目,99%是屎山 |
11 SanjinGG 3 天前 能出现这种代码的公司还有必要去? |
12 woodytang 2 天前 import { once, EventEmitter } from 'node:events'; import process from 'node:process'; //定义一个 事件喇叭 const ee = new EventEmitter(); process.nextTick(() => { //在异步队列里 喇叭发消息 ee.emit('myevent', 42); }); // 只处理一次的监听器,在主流程执行完后,会听到这个消息 const [value] = await once(ee, 'myevent'); console.log(value); const err = new Error('kaboom'); process.nextTick(() => { // 在异步队列里 喇叭发消息,但这次发的是一个 nodejs bug 设计,'error'是 nodejs hardcode 的 key ,你发这个消息相当于抛异常 ee.emit('error', err); }); try { //虽然你没有监听'error', 但是 nodejs 内部强迫你监听了 await once(ee, 'myevent'); } catch (err) { //虽然只是发了个消息,但是确抛了个异常 console.error('error happened', err); } 这个是大傻逼设计,消息是消息,异常是异常,违反 solid 原则,后来的 bunjs 运行时 不鼓励使用这种方式控制流程, 一般会使用 promise ,在异步方法里 throw 异常,然后使用 Controller ,控制异步任务的退出,也可以。 另外 现代化运行时框架 认为,用事件做控制流是反模式 是 anti pattern ,会把代码搞得很乱,不可追溯,一般采用 回调 来响应事件,更符合函数式编程 这样可以过面试吗? |
13 superhot OP @Zhuzhuchenyan 太可怕了…… @lqm @tearnarry 只要 AI 不能做到 100% 准确,就需要最终由人来把关,前提是你真的有能力做到这点。 @wuxilaoshiren 确实没必要纠结八股,但基础还是要有的。 @ssssiiiirren 顺序也许好推,但解释原理呢? @chenluo0429 也许有更好的方式考察这些基础,想问一下都有哪些“会引发典型的回调/异步等时序难以确定的场景” @Ketteiron 为什么呢?我的理解是 Node 中的很多类都基于 EventEmitter ,比如 Stream ,所以是很有必要去了解的。 @woodytang 受教了,“用事件做控制流是反模式 是 anti pattern ,会把代码搞得很乱,不可追溯,一般采用 回调 来响应事件,更符合函数式编程”,可以再深入解释一下这段话吗?回调响应事件,不就是 emitter.on 吗? |
14 woodytang 2 天前 @superhot 事件是这样的,emit("xxx",'咕咕咕'), listen('xxx',(text)=>{console.log("通知":text)}) 回调是这样的 async fn(handle){ handle('咕咕咕') } handle(text){ console.log("通知":text) } await fn(handle) -------- 它们本质都是 解决异步情况下,也就是在不确定什么时候的情况下,A 给 B 发消息的问题,, 事件是发出去不管的,它和监听者没有绑定关系 回调是是绑定的,可以追踪,可以测试的 但是事件可以批量订阅,你要发广播可以使用事件, 如果你只是要链式执行,使用回调 |
15 songray 2 天前 EventEmitter 和 process 都是 node 独有的,所以这个问题与其说是考察 JS 相关,不如说是考察 Node.js 实现... 更不用说 Node 项目八百年都用不上这些玩意。 浏览器环境下的事件循环就那几个 API ,根本没这么麻烦。 -------------- 再提一点,当初 Ryan 搞出 node:events 纯粹是因为当时的 JS 还没有 Promise ,observable ,stream 。只有回调、setTimeout 和 polling 。 EventEmitter 就是蛮荒时代不成熟的造物而已,现在面试还问这个,跟问 IE 浏览器兼容没什么区别,怕不是喝大了。 |
17 GiantHard 2 天前 > 最后,这真的不算八股,AI 当然可以解释清楚,但这么一段简单清晰的代码,你还不知所以然的话,那对着 Vibe Coding 出来的屎山,最后只能束手无策了。 确实不算八股,这算 NodeJS 实现细节;但既然 AI 可以解释清楚,为啥还会不知所以然? 我的一个感受是,现在 LLM 的知识广度已经远远超过人类了,考察一个人是否了解一项技术细节意义确实没以前那么重要。 > 只要 AI 不能做到 100% 准确,就需要最终由人来把关,前提是你真的有能力做到这点。 人也做不到 100% 准确,要不然就不会有 QA 团队了。之所以我们需要人类开发者,是因为人类开发者拥有一些 LLM 不具备的优势。 我觉得的人相对于 AI 的一个优势就是读不懂一些晦涩的代码,现在 LLM 读混淆后的 JS 都能把业务逻辑还原得八九不离十,这对于绝大多数人类来说是非常艰巨的任务。但也正是因为这点,人厌恶读起来不舒服的代码,在代码出现坏味道的时候,人的潜意识中就会产生抗拒情绪,这种情绪又会反过来让人避免编写有坏味道的代码: > 一个合格的开发应当知道一些会引发典型的回调/异步等时序难以确定的场景,然后在正常的开发过程中,别这么用他们 因此,只要 AI 生成的代码还需要人类参与维护,就需要有品味好的开发者充当 AI 代码的质检员,要么拒绝晦涩的代码进入代码库,要么在代码库出现坏味道的时候,能够自己动手或者指挥 AI 去重构、重写。 |
18 wangtian2020 2 天前 setTimeout(() => { console.log('a') setTimeout(() => console.log('b'), 1) }, 1) setTimeout(() => { console.log('c') }, 15) |
19 superhot OP @GiantHard 我个人理解是,只要 AI 无法消除幻觉,做到 100% 准确,就始终需要使用者具备判断 AI 是否准确的能力,而非照单全收。在这个例子里面,能断定 AI 可以解释清楚的前提是,你能完全理解以上提及的知识点。否则 AI 自圆其说,也许会把你说服,但却是在扯谎,那就非常糟糕了。 > 我的一个感受是,现在 LLM 的知识广度已经远远超过人类了,考察一个人是否了解一项技术细节意义确实没以前那么重要。 说实话,确实如此,但还是会有所顾虑,无法做到完全信任。另一方面,在 AI 的知识深度与广度都远超人类的今天,我们这些技术人员应该把精力放在哪里呢?有些迷茫。 |
20 KisekiRemi 2 天前 典中典,麻烦 OP 报一下你公司名,以后避开你司合作 |
21 caiyuan 2 天前 非常不喜欢使用发布订阅这种方式,维护非常难受。永远不清楚哪里发布了,哪里订阅了。 |
22 GiantHard 2 天前 > 我个人理解是,只要 AI 无法消除幻觉,做到 100% 准确,就始终需要使用者具备判断 AI 是否准确的能力,而非照单全收。在这个例子里面,能断定 AI 可以解释清楚的前提是,你能完全理解以上提及的知识点。否则 AI 自圆其说,也许会把你说服,但却是在扯谎,那就非常糟糕了。 我感觉你这里已经提到了一个比知识面广更重要的特质了,就是不轻易被 AI 说服,或者说,要具有批判性思维。 > 另一方面,在 AI 的知识深度与广度都远超人类的今天,我们这些技术人员应该把精力放在哪里呢?有些迷茫。 如果是为了工资而编程,那么不管有没有 AI ,技术人员是不是都应该把精力放在搞钱上?卖时间的就应该想办法让单位时间更值钱,卖体力的就应该想办法让劳动产出更值钱。 |