分享一道 Node.js 的面试题,考察 JS 相关的最基本的掌握程度,代码完全来自官方文档 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
superhot
V2EX    Javascript

分享一道 Node.js 的面试题,考察 JS 相关的最基本的掌握程度,代码完全来自官方文档

  •  
  •   superhot 3 天前 1875 次点击

    简述以下代码的输出结果,并解释执行过程:

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

    以上代码来自这里

    里面涉及到的基础知识点有:

    1. Promise
    2. async/await
    3. Node.js 中的微任务队列与 nexttick 队列
    4. CJS 与 MJS 的差异
    5. Node.js 中的 EventEmitter

    最后,这真的不算八股,AI 当然可以解释清楚,但这么一段简单清晰的代码,你还不知所以然的话,那对着 Vibe Coding 出来的屎山,最后只能束手无策了。

    23 条回复    2026-01-17 22:27:53 +08:00
    Zhuzhuchenyan
        1
    Zhuzhuchenyan  
       3 天前   1
    哈哈,这不是几年前我校招的时候面试官最“喜欢”的面试题嘛

    看我随手出一道,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);
    craftsmanship
        2
    craftsmanship  
       3 天前 via Android
    lqm
        3
    lqm  
       3 天前
    AI 既然清清楚楚,那怎么会堆出类似错误的屎山
    tearnarry
        4
    tearnarry  
       3 天前
    既然 AI 可以解释清楚
    1. 不会 Vibe Coding 出来屎山
    2. 如果有 Vibe Coding 出来的屎山,继续让 AI 修复即可
    4seasons
        5
    4seasons  
       3 天前
    @Zhuzhuchenyan 这个玩意儿我寻思着真的有人能分析出来吗?
    lanced
        6
    lanced  
       3 天前
    @Zhuzhuchenyan 太经典了
    wuxilaoshiren
        7
    wuxilaoshiren  
       3 天前
    典典典 前些年面试就喜欢问这个
    但是随着 AI 的到来,感觉看这些都没意义了
    ssssiiiirren
        8
    ssssiiiirren  
       3 天前
    哥们你这题目出的有问题,你这程序但凡要能运行的下去,就必须先打印 42 ,然后后面就能推出来了。
    如果不先打印 42 ,await 那里就卡住了。
    chenluo0429
        9
    chenluo0429  
       3 天前 via Android
    我面试的时候从来不会问这些。一个合格的开发应当知道一些会引发典型的回调/异步等时序难以确定的场景,然后在正常的开发过程中,别这么用他们
    Ketteiron
        10
    Ketteiron  
       3 天前
    我说个暴论,凡是用得上 EventEmitter 的项目,99%是屎山
    SanjinGG
        11
    SanjinGG  
       3 天前
    能出现这种代码的公司还有必要去?
    woodytang
        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 ,会把代码搞得很乱,不可追溯,一般采用 回调 来响应事件,更符合函数式编程


    这样可以过面试吗?
    superhot
        13
    superhot  
    OP
       2 天前
    @Zhuzhuchenyan 太可怕了……

    @lqm
    @tearnarry 只要 AI 不能做到 100% 准确,就需要最终由人来把关,前提是你真的有能力做到这点。

    @wuxilaoshiren 确实没必要纠结八股,但基础还是要有的。

    @ssssiiiirren 顺序也许好推,但解释原理呢?

    @chenluo0429 也许有更好的方式考察这些基础,想问一下都有哪些“会引发典型的回调/异步等时序难以确定的场景”

    @Ketteiron 为什么呢?我的理解是 Node 中的很多类都基于 EventEmitter ,比如 Stream ,所以是很有必要去了解的。

    @woodytang 受教了,“用事件做控制流是反模式 是 anti pattern ,会把代码搞得很乱,不可追溯,一般采用 回调 来响应事件,更符合函数式编程”,可以再深入解释一下这段话吗?回调响应事件,不就是 emitter.on 吗?
    woodytang
        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 发消息的问题,,
    事件是发出去不管的,它和监听者没有绑定关系

    回调是是绑定的,可以追踪,可以测试的

    但是事件可以批量订阅,你要发广播可以使用事件,

    如果你只是要链式执行,使用回调
    songray
        15
    songray  
       2 天前   1
    EventEmitter 和 process 都是 node 独有的,所以这个问题与其说是考察 JS 相关,不如说是考察 Node.js 实现...

    更不用说 Node 项目八百年都用不上这些玩意。

    浏览器环境下的事件循环就那几个 API ,根本没这么麻烦。

    --------------

    再提一点,当初 Ryan 搞出 node:events 纯粹是因为当时的 JS 还没有 Promise ,observable ,stream 。只有回调、setTimeout 和 polling 。

    EventEmitter 就是蛮荒时代不成熟的造物而已,现在面试还问这个,跟问 IE 浏览器兼容没什么区别,怕不是喝大了。
    superhot
        16
    superhot  
    OP
       2 天前
    @songray 看大家的说法,似乎现在没必要再去了解 EventEmitter 了?
    GiantHard
        17
    GiantHard  
       2 天前   1
    > 最后,这真的不算八股,AI 当然可以解释清楚,但这么一段简单清晰的代码,你还不知所以然的话,那对着 Vibe Coding 出来的屎山,最后只能束手无策了。

    确实不算八股,这算 NodeJS 实现细节;但既然 AI 可以解释清楚,为啥还会不知所以然?

    我的一个感受是,现在 LLM 的知识广度已经远远超过人类了,考察一个人是否了解一项技术细节意义确实没以前那么重要。

    > 只要 AI 不能做到 100% 准确,就需要最终由人来把关,前提是你真的有能力做到这点。

    人也做不到 100% 准确,要不然就不会有 QA 团队了。之所以我们需要人类开发者,是因为人类开发者拥有一些 LLM 不具备的优势。

    我觉得的人相对于 AI 的一个优势就是读不懂一些晦涩的代码,现在 LLM 读混淆后的 JS 都能把业务逻辑还原得八九不离十,这对于绝大多数人类来说是非常艰巨的任务。但也正是因为这点,人厌恶读起来不舒服的代码,在代码出现坏味道的时候,人的潜意识中就会产生抗拒情绪,这种情绪又会反过来让人避免编写有坏味道的代码:

    > 一个合格的开发应当知道一些会引发典型的回调/异步等时序难以确定的场景,然后在正常的开发过程中,别这么用他们

    因此,只要 AI 生成的代码还需要人类参与维护,就需要有品味好的开发者充当 AI 代码的质检员,要么拒绝晦涩的代码进入代码库,要么在代码库出现坏味道的时候,能够自己动手或者指挥 AI 去重构、重写。
    wangtian2020
        18
    wangtian2020  
       2 天前
    setTimeout(() => {
    console.log('a')
    setTimeout(() => console.log('b'), 1)
    }, 1)
    setTimeout(() => {
    console.log('c')
    }, 15)
    superhot
        19
    superhot  
    OP
       2 天前
    @GiantHard 我个人理解是,只要 AI 无法消除幻觉,做到 100% 准确,就始终需要使用者具备判断 AI 是否准确的能力,而非照单全收。在这个例子里面,能断定 AI 可以解释清楚的前提是,你能完全理解以上提及的知识点。否则 AI 自圆其说,也许会把你说服,但却是在扯谎,那就非常糟糕了。

    > 我的一个感受是,现在 LLM 的知识广度已经远远超过人类了,考察一个人是否了解一项技术细节意义确实没以前那么重要。

    说实话,确实如此,但还是会有所顾虑,无法做到完全信任。另一方面,在 AI 的知识深度与广度都远超人类的今天,我们这些技术人员应该把精力放在哪里呢?有些迷茫。
    KisekiRemi
        20
    KisekiRemi  
       2 天前
    典中典,麻烦 OP 报一下你公司名,以后避开你司合作
    caiyuan
        21
    caiyuan  
       2 天前
    非常不喜欢使用发布订阅这种方式,维护非常难受。永远不清楚哪里发布了,哪里订阅了。
    GiantHard
        22
    GiantHard  
       2 天前   1
    > 我个人理解是,只要 AI 无法消除幻觉,做到 100% 准确,就始终需要使用者具备判断 AI 是否准确的能力,而非照单全收。在这个例子里面,能断定 AI 可以解释清楚的前提是,你能完全理解以上提及的知识点。否则 AI 自圆其说,也许会把你说服,但却是在扯谎,那就非常糟糕了。

    我感觉你这里已经提到了一个比知识面广更重要的特质了,就是不轻易被 AI 说服,或者说,要具有批判性思维。

    > 另一方面,在 AI 的知识深度与广度都远超人类的今天,我们这些技术人员应该把精力放在哪里呢?有些迷茫。

    如果是为了工资而编程,那么不管有没有 AI ,技术人员是不是都应该把精力放在搞钱上?卖时间的就应该想办法让单位时间更值钱,卖体力的就应该想办法让劳动产出更值钱。
    racgas
        23
    racgas  
       22 小时 31 分钟前
    @caiyuan 意思是发布方不知道哪个订阅出了问题,订阅方不知道为什么会发布?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2776 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 12:59 PVG 20:59 LAX 04:59 JFK 07:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86