Unable to get callback from function - node.js

Trying to get the variable random_song from the function functions.random_song
Function:
functions.random_song = () => {
fs.readFile('auto_playlist.txt', 'utf8', function(err, data) {
if (err) {
console.log(`${`ERR`.red} || ${`Error fetching song from auto playlist ${err}`.red}`);
}
let songs = data.split('\n');
songs.splice(-1, 1);
var random = Math.floor(Math.random()*songs.length);
let random_song = (songs[random]);
return random_song;
})
}
Attempting to callback the random song
functions.random_song(song => {
console.log(song)
})
The code just return undefined Ideas?

Your function:
functions.random_song = (callback) => {
fs.readFile('auto_playlist.txt', 'utf8', function(err, data) {
if (err) {
// Do stuff
return callback(err);
}
// Do stuff
callback(null, random_song);
})
}
And then call it:
functions.random_song((err, song) => {
// Do stuff
});
You may want to read more about using Promise/async-await instead of callback.

functions.random_song = () => {
let data = fs.readFileSync('auto_playlist.txt', 'utf8');
let songs = data.split('\n');
songs.splice(-1, 1);
var random = Math.floor(Math.random()*songs.length);
let rand_song = (songs[random]);
return rand_song;
}
console.log(functions.random_song())
got it working, thanks for the help <3

Following is the code which use Promise to handle file read and data processing task asynchronously
const fs = require('fs')
const functions = {}
functions.random_song = () => {
return new Promise((resolve, reject) => {
fs.readFile('auto_playlist.txt', 'utf8', function(err, data) {
if (err) {
console.log(err)
reject(err)
}
let songs = data.split('\n');
songs.splice(-1, 1);
var random = Math.floor(Math.random()*songs.length);
let random_song = (songs[random]);
resolve(random_song);
})
})
}
functions.random_song()
.then(song => console.log('Song Name', song))
.catch(err => console.error('Error fetching songs', err))
console.log('It will be executed before promise is resolved')

fs.readFile is asynchronus and your function will return before the file is read. Use fs.readFileSync instead. Like this:
functions.random_song = () => {
const data = fs.readFileSync('auto_playlist.txt', 'utf8');
let songs = data.split('\n');
songs.splice(-1, 1);
var random = Math.floor(Math.random()*songs.length);
let random_song = (songs[random]);
return random_song;
}

Related

Force NodeJS to wait pipe stream in loop?

I'm trying to import .csv files into SQLite using NodeJS:
function get_files_array(path) {
let arr = [];
const files = fs.readdirSync(path);
for (const file of files) {
arr.push(path + file);
}
return arr;
}
let file_path = "./insert.sql";
let files_array = "./lots_of_csv/"
for (const file of get_files_array(files_array)) {
let csv2sql = CSV2SQL({
tableName: table_name,
});
let rstream = fs.createReadStream(file);
let wstream = fs.createWriteStream(file_path);
let stream = rstream.pipe(csv2sql).pipe(wstream);
stream.on("finish", () => {
const dataSql = fs.readFileSync(file_path).toString();
db.run("BEGIN TRANSACTION;");
db.run(dataSql, [], (err) => {
if (err) return console.error(err.message);
});
db.run("COMMIT;");
db.all("select count(*) from table_name", [], (err, rows) => {
if (err) return console.error(err.message);
rows.forEach((row) => {
console.log(row);
});
});
});
}
The problem is: as soon as it enters the stream.on("finish" line, it will start executing the next file in the loop simultaneously and crash.

Return result from python script in Node JS child process

Im trying to return the value from a python script from a nodejs child process and i just cant seem to get it to work, it prints in the console using console.log correctly as it should but only returns undefined, i was wondering if there is a way to directly return that value, or to parse the console.log results into a string.
var sys = require('util');
module.exports = function() {
this.getPlay = function getPlaylist(name) {
const childPython = spawn('python' ,['main.py', name]);
var result = '';
childPython.stdout.on(`data` , (data) => {
result += data.toString();
});
childPython.on('exit' , () => {
console.log(result);
});
}};
Python script is empty for now and prints "Hello 'name' "
Edit:
I tried to use promises and here is what i have:
(async function(){
function test(name) {
return new Promise((resolve , reject) => {
const childPython = spawn('python' ,['main.py', "He"]);
var result = '';
childPython.stdout.on(`data` , (data) => {
result += data.toString();
});
childPython.on('close' , function(code) {
t = result
resolve(result)
});
childPython.on('error' , function(err){
reject(err)
});
})};
var t;
await test(name);
console.log(t);
return t;
})();
Define it like this.
function getPlaylist(name) {
return new Promise((resolve , reject) => {
const childPython = spawn('python' ,['main.py', name]);
var result = '';
childPython.stdout.on(`data` , (data) => {
result += data.toString();
});
childPython.on('close' , function(code) {
resolve(result)
});
childPython.on('error' , function(err){
reject(err)
});
})
};
Remeber to use try...catch for it it gets rejected. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
async function runTest() {
try {
const playList = await getPlaylist();
console.log(playList);
} catch (err) {
}
}
runTest()
const {spawn} = require('child_process');
const getPythonScriptStdout = (pythonScriptPath) => {
const python = spawn('python', [pythonScriptPath]);
return new Promise((resolve, reject) => {
let result = ""
python.stdout.on('data', (data) => {
result += data
});
python.on('close', () => {
resolve(result)
});
python.on('error', (err) => {
reject(err)
});
})
}
getPythonScriptStdout('./python.py').then((output) => {
console.log(output)
})
python.py file
print("hi from python")

Get value out of function in Node.js

I don't know about the correct title for my problem. I just need a value going out of a function, just like return but I think it's not same.
i have code snippet from controller in adonisjs framework:
var nmeadata = "";
jsondata.forEach(element => {
var x = element.nmea
var buff = new Buffer(x, 'base64')
zlib.unzip(buff, (err, res) => {
if(err)
{
//
}
else
{
nmeadata += "greed island"
nmeadata += res.toString()
}
})
});
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
})
I need the result of unzipped string data that inserted to nmeadata from zlib function then send it to view. But, for this time, even I cannot displaying a simple output like greed island to my view.
thank you.
UPDATE
Still not working after using promises:
class NmeaController {
async index({view})
{
const datanmea = await NmeaModel.all()
const jsondata = datanmea.toJSON()
var promises = [];
var nmeadata = "";
jsondata.forEach(element => {
promises.push(
new Promise(resolve => {
let x = element.nmea
let buff = new Buffer(x, 'base64')
zlib.unzip(buff,
(err, res) => {
if (err) {
//
} else {
nmeadata += "test add text"
// nmeadata += res.toString()
}
//im also try using resolve() and resolve("any text")
resolve(nmeadata);
})
}
)
)
});
await Promise.all(promises);
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
});
}
UPDATE AUGUST 22 2019
i'm already tried solution from maksbd19 but still not working
class NmeaController {
async index({view})
{
const datanmea = await NmeaModel.all()
const jsondata = datanmea.toJSON()
var promises = [];
var nmeadata = "";
jsondata.forEach(element => {
promises.push(
new Promise(resolve => {
let x = element.nmea
let buff = new Buffer(x, 'base64')
zlib.unzip(buff,
(err, res) => {
if (err) {
// since you are interested in the text only, so no need to reject here
return resolve("");
}
return resolve("greed island")
})
}
)
)
});
const result = await Promise.all(promises); // this will be an array of results of each promises respectively.
nmeadata = result.join(""); // process the array of result
return view.render('admin.index', {
data: datanmea.toJSON(),
nmea: nmeadata
});
}
}
I'd suggest two things-
modify zlib.unzip callback function to resolve properly;
(err, res) => {
if (err) {
// since you are interested in the text only, so no need to reject here
return resolve("");
}
return resolve(res.toString())
}
retrieve the final data from the result of Promise.all
const result = await Promise.all(promises); // this will be an array of results of each promises respectively.
nmeadata = result.join(""); // process the array of result
In this approach every promise will resolve and finally you will get the expected result in array.

Can't return data from callback function at NodeJS

When i create new variable and assign callback function, But data cannot return from callback function. Undefined is occurring at new variable.
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
console.log(s);
var s is undefined! ....
You are mixing up callback and Promise which are two different way to handle asynchronous calls.
I recommend you to use of Promises because they are simpler and the present and future of javascript.
Using async/await which is the next step after Promises
const user = {
find: () => ['jon', 'kalessi', 'jorah'],
};
async function getUsers() {
return (await user.find({})) || [];
}
async function myJob() {
const users = await getUsers();
console.log(users);
// Put your next code here
}
myJob();
Full promise :
const user = {
find: () => new Promise((resolve) => resolve(['jon', 'kalessi', 'jorah'])),
};
user.find({})
.then((docs = []) => {
console.log(docs);
// Put you next code here, you can use of the variable docs
})
.catch((err) => {
console.log(err);
});
Full callback :
const user = {
find: (_, callback) => callback(false, ['jon', 'kalessi', 'jorah']),
};
user.find({}, (err, docs = []) => {
if (err) {
console.log(err);
} else {
console.log(docs);
// Put you next code here, you can use of the variable docs
}
});
I think user.find returning the promise. you can do like this.
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
Promise.all(s)
.then(result => {
console.log(result);
});
Otherwise you can also use await Like this:
async function abc(){
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = await user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
}
because await worked with async thats why i put it into async function.

How do I run multiple node streams in sequence?

Given two streams, stream1, stream2, how can I run them in sequence, throwing if any fails?
I'm looking for something simpler than this:
stream1.on('end',function(){
stream2.on('end',done);
});
stream1.on('error',function(error){done(error);});
stream2.on('error',function(error){done(error);});
Thanks.
there are some ways to do that, check next link, it gonna help to how to write some code in node in a elegant way:
Node.js FLOW
Hope it what you need.
Depending on your use case you could combine the two streams into one by using the multistream module.
Multistreams are constructed from an array of streams
var MultiStream = require('multistream')
var fs = require('fs')
var streams = [
fs.createReadStream(__dirname + '/numbers/1.txt'), // contains a single char '1'
fs.createReadStream(__dirname + '/numbers/2.txt'), // contains a single char '2'
fs.createReadStream(__dirname + '/numbers/3.txt') // contains a single char '3'
]
MultiStream(streams).pipe(process.stdout) // => 123
In case combining streams does not fit the use case you could build your stream on end event sending functionality on your own
const fs = require('fs');
var number1 = fs.createReadStream('./numbers1.txt')
.on('data', d => console.log(d.toString()));
var number2 = fs.createReadStream('./numbers2.txt')
.on('data', d => console.log(d.toString()));
onEnd([number1, number2], function(err) {
console.log('Ended with', err);
});
function onEnd(streams, cb) {
var count = streams.length;
var ended = 0;
var errored = null;
function shouldEnd() {
ended++;
if (errored) { return; }
if (count == ended) {
cb();
}
}
function endWithError(err) {
if (errored) { return; }
errored = true;
cb(err);
}
streams.forEach(s => s
.on('end', shouldEnd)
.on('error', endWithError)
);
}
The onEnd function can be used to wait for a stream array to end or in case an error event is emitted for the first emitted error event.
Try do it with async functions:
const { createReadStream } = require("fs")
async function main() {
const stream1 = createReadStream(__dirname + "/1.txt")
await pipe(stream1, process.stdout)
const stream2 = createReadStream(__dirname + "/2.txt")
await pipe(stream2, process.stdout)
const stream3 = createReadStream(__dirname + "/3.txt")
await pipe(stream3, process.stdout)
}
async function pipe(tap, sink) {
return new Promise((resolve, reject) => {
tap.pipe(sink, { end: false })
tap.on("end", resolve)
tap.on("error", reject)
})
}
Try do it with Promise
function doStream1(cb) {
// put those operation on stream1 in the callback function...
cb && cb();
var p = new Promise(function(resolve, reject) {
stream1.on( 'end', resolve );
stream1.on( 'error', reject );
});
return p;
}
function doStream2(cb) {
// some operation on stream2 on callback
cb && cb();
var p = new Promise(function(resolve, reject) {
stream2.on( 'end', resolve );
stream2.on( 'error', reject );
});
return p;
}
doStream1(cb).then(function() {
return doStream2(cb);
}).catch(function(e) {
// error handling is here
});
Try the code below(sequenceStream function). The thing which I am worried about is error handling, but it should work.
Also, if you use Node < 10.* then you need end-of-stream instead of stream.finished
const stream = require('stream');
const process = require('process')
const { promisify } = require('util')
const fromList = (lst) => new stream.Readable({
read() {
if (lst.length) {
this.push(String(lst.shift()))
} else {
this.push(null)
}
}
})
const _finished = promisify(stream.finished)
const sequenceStream = (streams) => {
const resultingStream = new stream.PassThrough()
let isNext = Promise.resolve()
for(const [i, curStream] of streams.entries()) {
isNext = isNext.then(() => {
curStream.pipe(resultingStream, {end: i === streams.length -1})
return _finished(curStream)
}).catch((err) => {
resultingStream.write(err)
})
}
return resultingStream
}
sequenceStream([
fromList([1, 2, 3, 4]),
fromList([5, 6, 7, 8]),
fromList([9, 10])
]).pipe(process.stdout)

Resources