NodeJS reading files and putting lines as function arguments - node.js

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
)

Related

How to mock 'readline.createInterface' in jest tests

I need to test 'readline.createInterface'.
Below is the code that I need to test:
private createReadStreamSafe(filePath: string): Promise<fs.ReadStream> {
return new Promise((resolve, reject) => {
const fileStream = fs.createReadStream(filePath)
console.log('file Stream')
fileStream
.on('error', () => {
reject('create read stream error')
})
.on('open', () => {
resolve(fileStream)
})
})
}
async start() {
const fileStream = await this.createReadStreamSafe(this.filePath)
const rl = readline.createInterface({
input: fileStream,
output: process.stdout,
terminal: false
})
for await (const line of rl) {
...
}
}
I tried following code:
it('should work', async () => {
const mockedReadStream = new Readable()
jest.spyOn(fs, 'createReadStream').mockReturnValue(mockedReadStream as any)
jest.spyOn(readline, 'createInterface').mockImplementation(() => {
const lines = ['text', 'text2', 'text3']
return {
[Symbol.asyncIterator]() {
return {
i: 0,
next: () => {
if (this.i < 3) {
return Promise.resolve({ value: lines[this.i++], done: false })
}
return Promise.resolve({ done: true })
}
}
}
} as any
})
const app = new App('myFile.txt')
let promise = app.start()
mockedReadStream.emit('open')
await expect(promise).resolves.toBe(undefined)
})
But following code is never reached
for await (const line of rl) {
...
}
Is there a way to mock readline.createInterface and then it works with the for await (const line of rl)?
The issue is: the async iterable object is not triggered during the test.
Solution, we can just use an array in the mock, like this:
jest.spyOn(readline, 'createInterface').mockImplementationOnce(() => {
return ['text1', 'text2'] as any
})
Since for await (const item of iterable) works for async iterable objects as well sync iterables. With sync iterables, they will be executed automatically.

Node.js fast-csv synchronously read file and return array

I am trying to read a .csv file and to assign it to an array with this code:
const fs = require("fs");
const csv = require("#fast-csv/parse");
let data = []
csv
.parseFile("./downloads/aggiornamento.csv", { skipRows: 2 })
.on("error", (error) => console.error(error))
.on("data", (row) => {
let obj = {};
obj["item_id"] = row[2];
obj["qty"] = row[20];
data.push(obj);
console.log(obj);
})
.on("end", function () {
return data;
});
However, the file read by fast-csv is a stream, hence I can only process row by row.
I need the full array in a second moment to call some API service, hence I would like to read the file synchronously, appending very row, and returning the array at the end, so that, in a second moment, I can wrap the previous code in a function, and obtain the array in a way such as:
const data = readCSVSynchronously()
How can I do it with fast-csv?
You can't turn an asynchronous function into a synchronous one.
You could, however, wrap that eventful reading code into a promise, which you can then use in a promise/async context, like so:
const fs = require("fs");
const csv = require("#fast-csv/parse");
function readCsv(path, options, rowProcessor) {
return new Promise((resolve, reject) => {
const data = [];
csv
.parseFile(path, options)
.on("error", reject)
.on("data", (row) => {
const obj = rowProcessor(row);
if (obj) data.push(obj);
})
.on("end", () => {
resolve(data);
});
});
}
async function doThings() {
const data = await readCsv(
"./downloads/aggiornamento.csv",
{ skipRows: 2 },
(row) => ({ item_id: row[2], qty: row[20] }),
);
// use data in API...
}
Callback functions can be wrapped with promise.
const fs = require("fs");
const csv = require("#fast-csv/parse");
let data = []
let readCSVSynchronously = async () = {
return new Promise(reject, resolve => {
const results = [];
csv
.parseFile("./downloads/aggiornamento.csv", { skipRows: 2 })
.on("error", (error) => console.error(error)
return reject(error);
)
.on("data", (row) => {
// Do your logic
results.push(row);
})
.on("end", function () {
resolve(results)
});
});
}
const totalResults = await readCSVSynchronously();

Asking multiple questions with readline nodejs

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

how to make asynchrone synchrone => how to test asynchronous treatement (ie write file)

I a writing unit test (mocha chai) on a file manipulation function
I am a beginner in Node.Js but used to other languages
I use typescript but sample is in JS
here is my problem:
A function writes in a file
when the program read the file a bit later the file is empty
when I look when the program is done the file is filled
To go fast a sample code
const fs = require("fs");
function test() {
let fss = fs.createWriteStream("file.txt", { flags: 'a+' });
fss.write("bla");
fss.end(() => {
fss.destroy();
});
}
test();
let val = fs.readFileSync('file.txt', 'utf8');
console.log(val); // empty
But it's not the first time I meet the problem. What is the good practice : promisify, events (I don't really like everything as a callback) ?
A couple of alternatives:
Call the code you need when done with the write stream
const fs = require("fs");
function test() {
let fss = fs.createWriteStream("file.txt", { flags: 'a+' });
fss.write("bla");
fss.end(() => {
fss.destroy();
let val = fs.readFileSync('file.txt', 'utf8');
// do stuff here with val
});
}
test();
Return a Promise in test
const fs = require("fs");
function test() {
return new Promise((resolve, reject) => {
let fss = fs.createWriteStream("file.txt", { flags: 'a+' });
fss.write("bla");
fss.end(() => {
fss.destroy();
resolve();
});
});
}
test().then(() => {
let val = fs.readFileSync('file.txt', 'utf8');
// do stuff here with val
});
Use the async / await syntax
const fs = require("fs");
async function test() {
await new Promise((resolve, reject) => {
let fss = fs.createWriteStream("file.txt", { flags: 'a+' });
fss.write("bla");
fss.end(() => {
fss.destroy();
resolve();
});
});
}
await test();
let val = fs.readFileSync('file.txt', 'utf8');
// do stuff here with val

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

Resources