Asking multiple questions with readline nodejs - node.js

I am attempting to get both username and password from the CLI in a puppeteer project. I get it to ask one question and can use the value just fine, but when I do the second it just freezes on the input. It is almost like it is not actually closing and returning. I cannot seem to figure out what I am missing. I tried to declare the interface in the question method and then destroy it when close is called, but that did not work. I feel like I am close, but I cannot figure out what I am missing.
const login = require('../common/login.js');
userId = await login.getUserId();
console.log(userId) //works
password = await login.getPassword();
console.log(password) //does not work
login.js
const readline = require("readline").createInterface({
input: process.stdin,
output: process.stdout
});
var methods = {};
const question = (promptText) => {
let response;
readline.setPrompt(promptText)
readline.prompt();
return new Promise((resolve, reject) => {
readline.on('line', (userInput) => {
console.log('hi');
response = userInput;
readline.close();
});
readline.on('close', () => {
console.log('bye');
resolve(response);
})
})
};
methods.getUserId = async() => {
let username = question("Username: ");
return username;
}
methods.getPassword = async() => {
let password = question("Password: ");
console.log(password);
return password;
}
module.exports = methods;

The readline.close() will close the streams. The below code works for me
const rli = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
function input(prompt) {
return new Promise((callbackFn, errorFn) => {
rli.question(prompt, (uinput)=> {
callbackFn(uinput);
}, ()=> {
errorFn();
});
});
}
const main = async () => {
amt = await input("Enter the amount to check: ");
doSomething(amt);
uname = await input("Enter your name: ");
doSomethingElse(uname);
rli.close();
};
main();

Related

NodeJS reading files and putting lines as function arguments

How can I put data from an external txt file to function argument? I get undefined because reading files is asynchronous and the values which I give to the functions are undefined. How can I make it work? Each line is one variable.
let numberOfElephants;
let massOfElephants;
let elephantsOrderGiven;
let proposedOrder;
const readFile = () => {
return new Promise((resolve, reject) => {
const lineArray = [];
const readInterface = readline.createInterface({
input: fs.createReadStream("./slo1.in"),
output: process.stdout,
terminal: false,
});
readInterface
.on("line", (line) => {
lineArray.push(line);
})
.on("close", () => {
resolve(lineArray);
});
});
};
async function readAsync() {
const res = await readFile();
numberOfElephants= res[0],
massOfElephants= res[1],
elephantsOrderGiven= res[2],
proposedOrder= res[3],
}
console.log("read");
readAsync();
const sortElephants = (
numberOfElephants,
massOfElephants,
elephantsOrder,
directorsOrder
) => {}
sortElephants(
numberOfElephants,
massOfElephants,
elephantsOrderGiven,
proposedOrder
)

How to corect work with 'process.stdin' in node 'worker_threads'

How to correct work with process.stdin in node worker_threads ...
I want to enter some value (code), but is stoped on input .. ?
tried 2 methods, but both same, stopped on input.
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
(async () => {
if (isMainThread) {
const worker = new Worker(__filename, {
stdin: true,
// stdout: true,
workerData: { text: 'Enter Code: ' }
});
} else {
console.log(workerData.text);
// Method-1
// answer = await new Promise(resolve => {
// process.stdin.once('data', (chunk) => {
// const code = chunk.toString().trim();
// console.log(`Captcha Code : ${code}`);
// resolve(code);
// });
// });
// Method-2
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
answer = await new Promise(resolve => {
rl.question('Code: ', (answer) => {
console.log(`Answer: ${answer}`);
rl.close();
resolve(answer);
});
});
}
})();

Puppeteer getting element from elementHandle causing protocol error

I'm trying to scrape a certain facebook page for its posts written by a certain user and starting with a certain word.
const puppeteer = require('puppeteer');
async function findPosts(page) {
const USERNAME = 'test123';
const posts = await page.$$('.userContentWrapper');
return posts.filter(async post => {
try {
let usernameElement = await post.$('.fwb');
let username = await page.evaluate(element => element.textContent, usernameElement);
if (username === USERNAME) {
let postElement = await post.$('[data-testid="post_message"] p');
let postContent = page.evaluate(element => element.textContent, postElement);
return /\[test \d+\]/.test(postContent);
}
return false;
} catch(e) {
console.log(e);
return false;
}
});
}
(async () => {
const browser = await puppeteer.launch({
headless: false
});
const page = await browser.newPage();
await page.goto('https://www.facebook.com/groups/groupid/');
const pageTitle = await page.title();
console.log(pageTitle);
const posts = await findPosts(page);
console.log(posts);
await browser.close();
})();
I'm getting
Error: Protocol error (Runtime.callFunctionOn): Target closed. when
I'm trying to get the usernameElement
at this line:
let usernameElement = await post.$('.fwb');
Not sure what's going wrong here, any suggestions?
The problem is that the filter function does not work with Promises. So the return posts.filter(...) will immediately return and after that the browser is closed. Therefore, when you try to run the $ function on the page, the page does not exist anymore and you get the Target closed error.
To make it work with async/await syntax, you could use a simple loop instead:
async function findPosts(page) {
const USERNAME = 'test123';
const posts = await page.$$('.userContentWrapper');
const postsToReturn = [];
for (let post of posts) {
/* ... if else logic */
postsToReturn.push(post); // instead of return true
}
return postsToReturn;
}

Node.js readline inside of promises

I'm trying to use the node.js package readline to get user input on the command line, and I want to pipe the entered input through promises. However, the input never gets through the then chain. I think the problem could come from the fact that the promises are fulfilled in the callback method, but I don't know how to solve that problem.
An example of this problem looks like this:
import rlp = require('readline');
const rl = rlp.createInterface({
input: process.stdin,
output: process.stdout
});
let prom = new Promise(resolve => {
rl.question('Enter input: ', input => rl.close() && resolve(input));
});
prom
.then(result => { console.log(result); return prom; })
.then(result => { console.log(result); return prom; })
.then(result => console.log(result));
If run in node.js, the question will appear once, after input has been entered the program just stops. I want it to wait until the first input has been entered, then it should print this input and ask for the next input.
Thanks in advance!
Once your promise is resolved, there's no use of waiting for that again. I also moved the rl.close() call to the end, as it's needed to be called only once.
const rlp = require('readline');
const rl = rlp.createInterface({
input: process.stdin,
output: process.stdout
});
function ask() {
return new Promise(resolve => {
rl.question('Enter input: ', input => resolve(input));
});
}
ask()
.then(result => { console.log(result); return ask(); })
.then(result => { console.log(result); return ask(); })
.then(result => { console.log(result); rl.close() });
Here's an answer from this question here for which I deserve no credit.
// Function
function Ask(query) {
const readline = require("readline").createInterface({
input: process.stdin,
output: process.stdout
})
return new Promise(resolve => readline.question(query, ans => {
readline.close();
resolve(ans);
}))
}
// example useage
async function main() {
var name = await Ask("whats you name")
console.log(`nice to meet you ${name}`)
var age = await Ask("How old are you?")
console.log(`Wow what a fantastic age, imagine just being ${age}`)
}
main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const ask = (query) => new Promise((resolve) => rl.question(query, resolve));
ask('A: ').then(async (a) => {
const b = await ask('B: ');
const c = await ask('B: ');
console.log(a, b, c);
rl.close();
});
rl.on('close', () => process.exit(0));
Node.js 17 is here with new promise-based APIs for readline module:
import * as readline from 'node:readline/promises'
import { stdin as input, stdout as output } from 'process'
const rl = readline.createInterface({input, output})
const answer = await rl.question('What do you think of Node.js? ')
console.log(`Thank you for your valuable feedback: ${answer}`)
rl.close()
https://nodejs.org/api/readline.html#readline
node prompt.mjs
import { createInterface as createQuestionInterface } from 'readline';
const rl = createQuestionInterface({
input: process.stdin,
output: process.stdout
});
function questionLine(multiline, resolve, i, input, rl) {
if (!multiline) {
resolve(i);
} else {
if (input && !i) {
resolve(input);
} else {
return input + i + "\r\n";
}
}
return input;
}
function promptMultiLine(questionText) { // This is async by returning promise
return prompt(questionText, true);
}
async function prompt(questionText, multiline = false) {
return await (new Promise((resolve, reject) => {
let input = '';
rl.question(`${questionText}: `, (i) => {
input = questionLine(multiline, resolve, i, input, rl);
});
rl.on('line', (i) => {
input = questionLine(multiline, resolve, i, input, rl);
});
}));
}
async function run() {
const question = prompt("please enter response [enter to complete]");
console.log(question);
const questionMultiLine = promptMultiLine("please enter response [enter text and enter twice]");
console.log(questionMultiLine);
}
run();

Async await not working in forEach

I have the following code:
async function goodUsers(users) {
const filteredUsers = [];
users.forEach(async (userInstance) => {
console.log('TEST1');
const fromUserContacts = await db.userContactInvite.findAll({
where: {
fromUserId: userInstance.id,
},
});
console.log('TEST2');
await fromUserContacts.forEach((fromUserContact) => {
console.log('TEST3');
const userJson = fromUserContact.toJSON();
userJson.contactState = 'INVITATION_SENT';
filteredUsers.push(userJson);
});
console.log('TEST4');
});
console.log('FILTERED', filteredUsers);
return filteredUsers;
}
When I call goodUsers I get the following output:
TEST1
FILTERED
TEST2
TEST3
TEST4
FILTERED should be last (obviously).
I tried various options but I seem to be not understanding something here. Do you guys notice what's going on?
This's a correct behavior, async/await only affect the function where they are used. So you need to replace forEach which calls callback for each element, to for operator:
async function goodUsers(users) {
const filteredUsers = [];
for(user in users) {
console.log('TEST1');
const fromUserContacts = await new Promise(resolve => setTimeout(() => resolve(['c1', 'c2']), 500));
console.log('TEST2');
fromUserContacts.forEach(fromUserContact => {
console.log('TEST3');
filteredUsers.push('json');
});
console.log('TEST4');
}
console.log('FILTERED', filteredUsers);
return filteredUsers;
}
goodUsers(['u1', 'u2']);

Resources