【问题标题】:different between callback and promise in node.jsnode.js中的回调和承诺之间的区别
【发布时间】:2025-11-26 20:45:02
【问题描述】:

我的代码:

const readline = require('readline');

function scan(callback) {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
        prompt: '> ',
    });

    rl.prompt();
    rl.on('line', (line) => {
        callback(line);
        rl.close();
    }).on('close', () => {
        process.exit(0);
    });
}

scan(data => {
    console.log('data: ', data);  // can console
});

我使用了回调,它可以控制台你输入的数据,但是当我使用promise时它不会控制台:

function scan() {
    return new Promise((resolve, reject) => {
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
            prompt: '> ',
        });

        rl.prompt();
        rl.on('line', (line) => {
            resolve(line);  
            rl.close();
        }).on('close', () => {
            process.exit(0);
        });
    });
}

scan().then(data => {
    console.log('data: ', data);   // can not console
});

这是怎么回事?callback和promise有什么不同?

【问题讨论】:

  • @cometguy 我读过,但我认为这不是我的问题的答案。
  • 可以多次调用回调(例如在示例中的每一行)。一个承诺只能解决(履行或拒绝)一次。它可能适合在整个文件完成时发出信号,但您不能使用它来处理行。
  • 如果你问我,这段代码只是结构错误。您正在函数内执行process.exit(),但期望其结果在退出实际发生之前由调用者处理。这只是一个糟糕的编码设计,由于异步处理顺序的细微差别,它在这里咬了你。修复设计,使操作的顺序由代码直接指定和控制,而不是留给如何安排不同类型的异步操作的细微差别。此外,在大多数情况下,您可以让程序在无事可做时自行退出。

标签: javascript node.js callback promise


【解决方案1】:

堆栈和队列问题,您的回调与 rl.close 在同一个堆栈上,但解析处理程序不在(那个在队列中)。

例如:

new Promise(resolve=>resolve())
.then(_=>console.log("last"))//resolve handler is going on queue
console.log("first")//this is on the same stack so logs
//nothing more on the stack so get stuff from queue

简单的解决方案是在记录日志的解析处理程序后关闭队列:

function scan() {
  return new Promise((resolve, reject) => {
      const rl = readline.createInterface({
          input: process.stdin,
          output: process.stdout,
          prompt: '> ',
      });

      rl.prompt();
      rl.on('line', (line) => {
          resolve(line);  
          //put close on queue
          Promise.resolve()
          .then(
            _=>rl.close()
          );
      }).on('close', () => {
          process.exit(0);
      });
  });
}

scan().then(data => {
  console.log('data: ', data);   // can not console
});

堆栈和队列videoducumentation

【讨论】:

  • 堆栈?队列?你是说栈和堆吗?
  • @Bergi stack as in callstack queue as in event loop or message queue
  • @Bergi 也许说解析处理函数不在当前调用堆栈中但保存在堆中并在从队列中获取关联消息时调用会更正确,因为该函数应该处理该消息。在调用堆栈为空之前不会从队列中取出消息,因此首先调用 rs.close() 和 on close 处理程序。如果 readline 包的实现方式不同,则关闭处理程序可能会在解析处理程序之后调用(例如:其中有一个超时以清除调用堆栈并让步到另一条消息)。
  • 是的,我想这样会更正确。或者换一种说法:close 事件是同步触发的,而resolve 总是异步触发 Promise 处理程序。
  • @HMR callback() 和 rl.close() 是同步的,因为它们在一个堆栈中,resolve() 和 rl.close() 是异步的,因为 resolve 在回调队列中,但 rl.close 在堆 。对吗?
【解决方案2】:

如需更详细的答案,请参阅 HMR 的回复。

显然您在解决承诺后立即调用 rl.close() 。看看你的关闭事件处理程序:

 process.exit(0);

显然它将停止所有执行并终止您的程序。事件虽然你的承诺得到解决,但退出被更快地调用(事件循环的正常行为)。

所以要解决您的问题:删除关闭事件处理程序。在每个 promise 得到解决后,您的程序将正常终止。

【讨论】:

  • 但这并不能解释,回调在关闭处理程序中也有process.exit(0)。问题是resolve() 最终会从队列中调用resolveHandler,因此堆栈中的下一个是rl.colse() 这不会发生在回调中,调用回调和rl.close() 在同一个堆栈上。
最近更新 更多