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 · 23 小时 17 分钟前 · 1454 次点击

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

    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 出来的屎山,最后只能束手无策了。

    22 条回复    2026-01-16 14:16:29 +08:00
    Zhuzhuchenyan
        1
    Zhuzhuchenyan  
       22 小时 58 分钟前   ❤️ 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  
       22 小时 57 分钟前 via Android
    @Zhuzhuchenyan 🆘啊
    lqm
        3
    lqm  
       22 小时 53 分钟前
    AI 既然清清楚楚,那怎么会堆出类似错误的屎山
    tearnarry
        4
    tearnarry  
       22 小时 48 分钟前
    既然 AI 可以解释清楚
    1. 不会 Vibe Coding 出来屎山
    2. 如果有 Vibe Coding 出来的屎山,继续让 AI 修复即可
    4seasons
        5
    4seasons  
       22 小时 44 分钟前
    @Zhuzhuchenyan 这个玩意儿我寻思着真的有人能分析出来吗?
    lanced
        6
    lanced  
       21 小时 25 分钟前
    @Zhuzhuchenyan 太经典了
    wuxilaoshiren
        7
    wuxilaoshiren  
       21 小时 22 分钟前
    典典典 前些年面试就喜欢问这个
    但是随着 AI 的到来,感觉看这些都没意义了
    ssssiiiirren
        8
    ssssiiiirren  
       21 小时 22 分钟前
    哥们你这题目出的有问题,你这程序但凡要能运行的下去,就必须先打印 42 ,然后后面就能推出来了。
    如果不先打印 42 ,await 那里就卡住了。
    chenluo0429
        9
    chenluo0429  
       20 小时 57 分钟前 via Android
    我面试的时候从来不会问这些。一个合格的开发应当知道一些会引发典型的回调/异步等时序难以确定的场景,然后在正常的开发过程中,别这么用他们
    Ketteiron
        10
    Ketteiron  
       20 小时 40 分钟前
    我说个暴论,凡是用得上 EventEmitter 的项目,99%是屎山
    SanjinGG
        11
    SanjinGG  
       20 小时 6 分钟前
    能出现这种代码的公司还有必要去?
    woodytang
        12
    woodytang  
       18 小时 0 分钟前
    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
       17 小时 38 分钟前
    @Zhuzhuchenyan 太可怕了……

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

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

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

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

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

    @woodytang 受教了,“用事件做控制流是反模式 是 anti pattern ,会把代码搞得很乱,不可追溯,一般采用 回调 来响应事件,更符合函数式编程”,可以再深入解释一下这段话吗?回调响应事件,不就是 emitter.on 吗?
    woodytang
        14
    woodytang  
       16 小时 25 分钟前
    @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  
       7 小时 27 分钟前
    EventEmitter 和 process 都是 node 独有的,所以这个问题与其说是考察 JS 相关,不如说是考察 Node.js 实现...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    如果是为了工资而编程,那么不管有没有 AI ,技术人员是不是都应该把精力放在搞钱上?卖时间的就应该想办法让单位时间更值钱,卖体力的就应该想办法让劳动产出更值钱。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   5275 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 08:18 · PVG 16:18 · LAX 00:18 · JFK 03:18
    ♥ Do have faith in what you're doing.