nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
105.22k stars 28.5k forks source link

Promise readline question results in unsettled promise on abortion #53497

Open 029A-h opened 2 weeks ago

029A-h commented 2 weeks ago

Version

v22.2.0

Platform

Linux LX-LAPII 6.9.3-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 31 May 2024 15:14:45 +0000 x86_64 GNU/Linux

Subsystem

node:readline/promises

What steps will reproduce the bug?

It is not possible to properly handle a user's abortion of a promise readline question with SIGINT or Ctrl+D while rl.question waits for user input:

import * as readline from 'node:readline/promises';

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

try {
  const answer = await rl.question('>>> ');
} catch {}

How often does it reproduce? Is there a required condition?

Every time when a prompt is aborted with Ctrl+C or Ctrl+D.

What is the expected behavior? Why is that the expected behavior?

The expected behavior is to settle a promise on a readline close event, as shown in the following snippet:

import * as readline from 'node:readline';

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

let rej = () => {};
rl.on('close', () => {
  console.log('closing');
  rej();
});

const answer = await new Promise((r, j) => {
  rej = j;
  rl.question('>>> ', r);
}).catch(() => console.error('interrupted'));

What do you see instead?

The promise remains unsettled, resulting in a warning that cannot be caught:

>>> Warning: Detected unsettled top-level await at file:///tmp/bug.mjs:9
  const answer = await rl.question('>>> ');

Additional information

This issue is opened based on the discussion in [stackoverflow] How to properly abort Node readline promise question?

imanhpr commented 2 weeks ago

More Additional Information

According to the Nodejs documentation related to exit codes:

13 Unsettled Top-Level Await: await was used outside of a function in the top-level code, but the passed Promise never settled.

In the example below I expect to get 13 as an exit code in beforeExit and exit events but it doesn't.

import { createInterface } from "node:readline/promises";

process.on("exit", (code) => {
  console.log("\nexit:", code);
});

process.on("beforeExit", (code) => {
  console.log("\nbeforeExit:", code);
});

process.on("warning", (w) => {
  console.log("\nwar :", w);
});
const rl = createInterface({ input: process.stdin, output: process.stdout });

await rl.question("Enter something:");

Result :

Enter something:
beforeExit: 0

exit: 0
Warning: Detected unsettled top-level await at file:///home/imanhpr/Desktop/sandbox/node-box/sig.js:16
const data = await rl.question("Enter something:");

It's good to mention when I check the previous process exit code with echo $? it's 13 and it's fine.

DanielVenable commented 2 weeks ago

I could try to work on this. Can someone assign me?

RedYetiDev commented 2 weeks ago

Hi! If you have a solution, feel free to submit a PR!