I am writing a Cmus Remote that is browser based and uses Nodejs on the backend. Part of the app involves getting the currently playing song and displaying it to the user.
Currently it successfully runs the command gets the output stored into a variable properly, but the client side request runs before the callback of the server side function thus it retruns an empty string for the song.
Here is the code to better illustrate what I mean:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cmus Remote</title>
<script src="client.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body id="body">
</body>
</html>
client.js
"use strict";
window.onload = function () {
$.get("/songInfo", function(string){
alert(string);
});
};
server.js
var express = require('express');
var app = express();
var path = require('path');
var exec = require('child_process').exec;
var fs = require('fs');
var child;
var getSongCommand = "cmus-remote -Q | sed -n -e 's/^.*tag title //p'";
var getAlbumCommand = "cmus-remote -Q | sed -n -e 's/^.*tag album //p'";
var getArtistCommand = "cmus-remote -Q | sed -n -e 's/^.*tag artist //p'";
var song ="";
var album= "";
var artist = "";
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res){
res.sendFile(path.join(__dirname + '/index.html'));
});
app.get('/songInfo', function(req, res){
updateSongInfo(getSongCommand);
updateSongInfo(getAlbumCommand);
updateSongInfo(getArtistCommand);
var strings = [song, artist, album];
res.send(strings);
});
var server = app.listen(8080, function () {
console.log("Server online");
});
function updateSongInfo(command){
var exec = require('child_process').exec;
exec(command, function(error, stdout, stderr){
callback(command, stdout);
});
}
function callback(commandRan, output){
console.log("Commandran = " + commandRan);
console.log("Command output = " + output);
if(commandRan.includes("title")){
console.log("Updating song to " + output);
song = output;
}
if(commandRan.includes("album")){
album = output;
}
if(commandRan.includes("artist")){
artist = output;
}
// console.log("In callback");
// console.log(output);
return output;
}
To summarize, the ajax response is working properly, the command runs properly and the values are saved to the 3 global variables I have, but I am not sure how to set up the timing that the ajax request returns once the variables have values.
The problem is because exec is asynchronous, so exec will be called, the program will continue and return the still empty data to the caller and then later it will finish with the execution data now received.
To fix this you can use Promise and async/await.
app.get('/songInfo', async function(req, res){
await updateSongInfo(getSongCommand);
await updateSongInfo(getAlbumCommand);
await updateSongInfo(getArtistCommand);
var strings = [song, artist, album];
res.send(strings);
});
...
function updateSongInfo(command){
return new Promise((resolve, reject) => {
var exec = require('child_process').exec;
exec(command, function(error, stdout, stderr){
callback(command, stdout);
return resolve();
});
});
}
Calling resolve() inside the Promise will complete it, while calling reject() will throw an error. Also you can give those functions parameters as well.
Your updateSongInfo function runs asynchronously, so you're server is sending back a response before the update has completed. What you'll need to do is either implement Promises or a callback to run after those functions have completed. I would probably suggest that you not use global variables here, but instead return the result each time. Here's an example:
app.get('/songInfo', function(req, res) {
var song, artist, album;
updateSongInfo(getSongCommand, function(err, result) {
if (err) return res.send(err);
song = result;
updateSongInfo(getAlbumCommand, function(err, result) {
if (err) return res.send(err);
album = result;
updateSongInfo(getArtistCommand, function(err, result) {
if (err) return res.send(err);
artist = result
// Now your globals will be fulfilled
return res.send([song, artist, album]);
});
});
});
});
function updateSongInfo(command, cb){
var exec = require('child_process').exec;
exec(command, function(error, stdout, stderr){
callback(command, stdout, cb);
});
}
function callback(commandRan, output, cbFunction){
console.log("Commandran = " + commandRan);
console.log("Command output = " + output);
if(commandRan.includes("title")){
console.log("Updating song to " + output);
song = output;
}
if(commandRan.includes("album")){
album = output;
}
if(commandRan.includes("artist")){
artist = output;
}
// console.log("In callback");
// console.log(output);
return cbFunction(null, output);
}
Related
I have a MongoClient function which seems to be asynchronous in nature. Followed by child process function which I am explicitly declaring as synchronous. The cp takes a parameter nextPort, and the value of nextPort is assigned inside MongoClient. Since cp.execSync takes precedence over MongoClient it doesn't take the new value of the variable rather the value it was declared with.
app.js
var nextPort=0; //Declaration
MongoClient.connect(url1,{ useUnifiedTopology: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("mydb");
dbo.collection("mycol").distinct('port', function(err, result) {
if (err) throw err;
portArr=result;
console.log(portArr.length);
nextPort = portArr[portArr.length-1]+1; //New Value of nextPort
console.log(nextPort);
db.close();
});
});
console.log('Container Created\n');
console.log(nextPort);
const result3 = cp.execSync('docker run -d -p '+nextPort+':27017 -v '+volumeLoc+' --name '+containerName+' mongo:'+version);
//Taking the declaration value instead of new
console.log(result3.toString());
How do I execute MongoClient before cp.execSync. (Also, I'd really appreciate if the solution doesn't deal with promises)
Try to learn the async / await syntax for cleaner code. I dont know if this is just a script or an function, anyway, i wrapped it in an async IIFE:
(async () => {
var nextPort = 0; //Declaration
try {
var db = await MongoClient.connect(url1, { useUnifiedTopology: true });
var dbo = db.db("mydb");
var result = await dbo.collection("mycol").distinct("port");
portArr = result;
console.log(portArr.length);
nextPort = portArr[portArr.length - 1] + 1; //New Value of nextPort
console.log(nextPort);
db.close();
} catch (err) {
throw err;
}
console.log("Container Created\n");
console.log(nextPort);
const result3 = cp.execSync(
"docker run -d -p " +
nextPort +
":27017 -v " +
volumeLoc +
" --name " +
containerName +
" mongo:" +
version
);
//Taking the declaration value instead of new
console.log(result3.toString());
})();
You either need to call your cp.execSync inside the callback function you provided to MongoClient.connect or you should use async/await syntax, e.g.:
const client = await MongoClient.connect(url1, { useUnifiedTopology: true })
const result3 = cp.execSync(...)
Make sure your wrapper function (factory method to instantiate a client in
a separate module would be better approach) is async, take a look at the example
this is my first time asking questions and this is basically my last resort in finding some answers. Im a noob and beginner in javascript so please use simple terms with me.
So i have an issue. I dont know how to query.
- As in do i put all my queries in one script or do i have to split them up to different scripts.
- Right now, i have a server.js and i put all my codes in there. including queries. So how do i run just one of them.
- and also if there is such a thing for me to query for just another number like 4. Do i have to go back to the script to manually change from '110' to '4' or can i just enter it somewhere.
Some examples are:
//length of 110
db.collection.find({length: "110"}, function(err, collection) {
if( err || !collection) console.log("No collections with 110 in length");
else collection.forEach( function(length) {
console.log(length);
} );
});
//shows record of length 110 and length 340
var length = ['110', '340']
length = length.join('|');
var re = new RegExp(length, 'i')
db.collection.find({length:{$regex: re}}, function(err, collection) {
if( err || !collection ) console.log("User not found");
else collection.forEach (function(length){
console.log(length);
});
});
How do i query to only run for one of them in mongodb. Appericiate the help alot guys
You already know that node.js is used for making servers. So there are 2 ways you can query the database. Since you're new to node, I'm going to list both the ways :
1.A http GET for querying data (Standard Way)
You can write a simple http server and set a route for getting the type of data you want.I've written a small file so that you can understand:
var express = require('express');
var http = require('http');
var app = express();
var bodyParser = require('body-parser');
// Db settings
var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/my_database_name';
function getConnection(url, callback) {
MongoClient.connect(url, function(dbErr, dbRes) {
if(dbErr) {
return callback(err, null);
}
callback(null, dbRes);
});
}
// Configure app to use bodyparser
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
var port = process.env.PORT || 7013;
app.set('port', port);
app.use(function(req, res, next) {
console.log(req.ip+ ":"+port + req.url + " "+req.method);
next();
});
app.get('/getCityData', function(req, res, next) {
var cityName = req.query.city;
getConnection(url, function(conErr, db) {
if(conErr) {
return res.send("ERROR IN GETTING connection");
}
var cityCollection = db.collection('cities');
cityCollection.find({"city":cityName}).toArray(function(err, result) {
if(err) return res.send("error in querying data");
db.close();
res.send(result);
});
});
});
var httpServer = http.createServer(app).listen(port, function() {
console.log("Express server listening on port "+app.get('port'));
});
You can query the server using curl or postman like this :
2.Using command line arguments :
You can also do it the easy way using command line arguments by passing the parameter to query, but it's less flexible:
var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/my_database_name';
function getConnection(url, callback) {
MongoClient.connect(url, function(dbErr, dbRes) {
if(dbErr) {
return callback(err, null);
}
callback(null, dbRes);
});
}
var cityName = process.argv[2]; // that's the argument you'd receive
getConnection(url, function(conErr, db) {
if(conErr) {
return res.send("ERROR IN GETTING connection");
}
var cityCollection = db.collection('cities');
cityCollection.find({"city":cityName}).toArray(function(err, result) {
if(err) return res.send("error in querying data");
db.close();
console.log(result);
});
});
Pass that commandline argument like this while executing file :
I hope it helps
I have a mongodb database called pokemon with a collection called pokemons. Here is my attempt to write a function that will do a find() operation in mongodb:
'use strict';
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
// db url
var url = 'mongodb://localhost:27017/pokemon';
exports.getPokemonByName = function (name) {
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
var cursor = db.collection('pokemons').find({name: name});
// how to return json?
});
};
I then call this function in another file:
var express = require('express');
var router = express.Router();
router.get('/pokedex', function (req, res) {
res.jsonp(db.getPokemonByName('Dratini'));
})
This link is helpful in showing how to log mongodb data to the console by doing some sort of each() method on the cursor object, but I don't know how to return json through the getPokemonByName function. I tried to define an empty array on the root scope of the getPokemonByName function and push data into that array with each iteration of the .each method show in that link, but I think I still can't return that array because it happens after the fact.
BTW, I'm really just doing this for fun and to learn about MongoDB and Node.js, so I don't want to use or an ODM like Mongoose to do some of this work for me.
I was able to answer my question with help from node's native monogodb driver github page: See here.
In essence, what I did was to define my exported function within the MongoClient's connection function. For some reason I thought node exports had to be in the root of the module, but that's not the case. Here's a finished version:
'use strict';
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
// db url
var url = 'mongodb://localhost:27017/pokemon';
var findDocuments = function(db, callback) {
// Get the documents collection
var collection = db.collection('pokemons');
// Find some documents
collection.find({name: 'Dratini'}).toArray(function(err, docs) {
assert.equal(err, null);
// assert.equal(2, docs.length);
console.log("Found the following records");
callback(docs);
});
}
// Use connect method to connect to the Server
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected correctly to server");
findDocuments(db, function(docs) {
console.log(docs);
exports.getPokemonByName = function() {
return docs;
}
db.close();
});
});
And then in another file:
var express = require('express');
var router = express.Router();
router.get('/pokedex', function (req, res) {
res.jsonp(db.getPokemonByName());
});
Of course, this solution requires that I hardcode queries, but I'm okay with that for now. Will cross that bridge when I come to it.
Found a simple tweak for this. Let say the callback to the findOne returns result then you can convert the result to JSON object like this
result = JSON.parse(JSON.stringify(result))
Now you can access the result and its fields simply with the dot operator.
this may help
var cursor = db.collection('pokemons').find({name:name}).toArray(function(err,arr){
return arr;
});
You can use callbacks on find function to return the json.
Try
exports.getPokemonByName = function (name,callback) {
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
var cursor = db.collection('pokemons').find({name: name},function(err,result){
if(err)
{
callback(err,null);
}
if(result)
callback(null,result);
});
});
};
router.get('/pokedex', function (req, res) {
db.getPokemonByName('Dratini',function(err,result){
if(result)
{
res.jsonp(result);
}
});
})
I'm buidling an app with Node anb Mongodb Native. I'm working on a db module which i can require and call in other modules so that I end up using just one connection. The module db.js started out with this code:
var _db = null;
var getDb = module.exports.getDb = function(callback) {
if (_db) {
console.log('_db returned');
return callback(null, _db);
}
MongoClient.connect('mongodb://localhost:' + config.db.port + '/' + config.db.name, {native_parser: true}, function (err, db) {
if (err) return callback(err);
console.log('_db created');
_db = db;
callback(err, _db);
});
};
In my other modules that need a db connection I do this
db.getDb(function (err, connection) {
// Do something with connection
});
It works fine. But an unpleasant problem is that if my code would call getDb multiple times in a very short time span, I would end up with several copies of a connection. Like if I do my db.js requirements and getDb calls at the very beginning of all modules that need a db connection
I'm now thinking about controlling the calls to getDb by queuing them, so that only the absolute first call will create a connection and save it in _db. All later calls will get the created connection _db in return. I believe Async queue will help me with this...
The problem is that i dont understand how I write this with Async queue. The documentation is a little bit vague, and i dont find any better examples online. Maybe you can give me some hints. This is what i got so far...
var dbCalls = async.queue(function (task, callback) {
if (_db) {
console.log('_db returned');
return callback(null, _db);
}
MongoClient.connect('mongodb://localhost:' + config.db.port + '/' + config.db.name, {native_parser: true}, function (err, db) {
if (err) return callback(err);
console.log('Connected to mongodb://localhost:' + config.db.port + '/' + config.db.name);
_db = db;
callback(null, _db);
});
}, 1);
// I guess this .push() must be the exposed (exported) API for other modules to get a connection, but how do I return it to them,
dbCalls.push(null, function (err) {
console.log('finished processing foo');
});
dbCalls.push(null, function (err) {
console.log('finished processing bar');
});
I dont understand the object passed as first argument to .push() What should i use if for? Right now its null How do I pass on the connection and possible error all the way out to the module that made the call?
A quick and dirty solution without async.queue:
var _db = null;
var _err = null;
var _queue = [];
var _pending = false;
var getDb = module.exports.getDb = function(callback) {
if (_err || _db) {
console.log('_db returned');
return callback(_err, _db);
} else if (_pending) { // already a connect() request pending
_queue.push(callback);
} else {
_pending = true;
_queue.push(callback);
MongoClient.connect(..., function (err, db) {
_err = err;
_db = db;
_queue.forEach(function(queuedCallback) {
queuedCallback(err, db);
});
});
};
I've been looking for quite a while for a solution but haven't found anything yet.
I'm trying to emit a message from a server every time the server sees that a file has changed in a specified directory. However, instead of only emitting one message, it insists on emitting the same message three times. I am using chokidar to watch the directory, and inside of the 'change' event I emit the message.
Server side code:
var express = require('express')
, app = express()
, http = require('http')
, server = http.Server(app)
, io =require('socket.io')(server)
, chokidar = require('chokidar');
server.listen(1234);
app.use('/public', express.static( __dirname + '/public'));
app.get('/', function(request, response){
var ipAddress = request.socket.remoteAddress;
console.log("New express connection from: " + ipAddress);
response.sendfile(__dirname + '/public/index.html'); //Server client
});
var watcher = chokidar.watch("temp", {ignored: /[\/\\]\./, persistent: true});
watcher.on('change', function(path){
console.log(path + " has changed.");
fs.readFile(path,'utf8', function(err, data){
if(err) {
return console.log(err);
}
else
{
var json = JSON.parse(data), recPsec, type;
recPsec = json.data[0].values[0];
type = json.data[0].values[16];
var compiled = {
"recPsec" : recPsec,
"type" : type
}
var jsonMessage = JSON.stringify(compiled)
io.sockets.emit('message', JSON.stringify(jsonMessage));
console.log("Sent message");
}
});
});
watcher.on('unlink', function(path){
console.log('File: ', path, ' has been removed');
});
watcher.on('add', function(path){
console.log("hi");
fs.readFile(path,'utf8', function(err, data){
if(err) {
return console.log(err);
}
else
{
var json = JSON.parse(data), recPsec, type;
recPsec = json.data[0].values[0];
type = json.data[0].values[16];
var compiled = {
"recPsec" : recPsec,
"type" : type
}
var jsonMessage = compiled;
io.sockets.emit('message', JSON.stringify(jsonMessage));
console.log("message sent");
}
//fs.unlinkSync(path);
});
});
Client Side:
var socket = io.connect('http://localhost');
socket.on('message', function(data){
console.log(data);
var parsed = JSON.parse(data);
recPsecNew = parsed.recPsec;
typeNew = parsed.type;
analyze(recPsecNew, typeNew);
});
I am using socket.io in conjunction with express 4.
Chokidar is found here: https://github.com/paulmillr/chokidar
Logs from the console if I change the name of a file twice are shown here: http://s000.tinyupload.com/?file_id=95726281991906625675
Have you tried lodash's Function?
Probably you can use lodash.debounce function
According to its docs:
_.debounce(func, [wait=0], [options])
Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked. The debounced function comes with a cancel method to cancel delayed invocations. Provide an options object to indicate that func should be invoked on the leading and/or trailing edge of the wait timeout. Subsequent calls to the debounced function return the result of the last func invocation.