I encapsulated the nedb module in self-defined object:
var Record = function() {
var Datastore = require('nedb');
this.db = new Datastore({filename: 'record'});
this.db.loadDatabase();
};
And I want to define my own function to get all the objects in the database:
Record.prototype.getItems = function() {
var items = null;
this.db.find({}, function(err, docs) {
items = docs;
});
return items;
};
However, the variable "items" can't be assigned to variable "docs" and is always "null". I realize that this is caused by the asynchronous nature of JavaScript.But how can I get the variable "docs" out?
You can use promises and async/await syntax to omit the callback
Record.prototype.getItems = () => {
return new Promise((resolve, reject) => {
this.db.find({}, (err, doc) => {
err ? reject(err) : resolve(doc);
});
});
}
And then retrieve the data like so
async function whatever(){
const data = await Record.getItems();
}
I would also recommend using this wrapper module for nedb, called NeDB-promises ,which includes convenient features like built-in support for promises and events
You need a callback function that retrieves the data when it's ready, like this:
Record.prototype.getItems = function(callback) {
this.db.find({}, callback);
};
Then use it:
const record = new Record();
record.getItems((err, docs) => {
if (err) return handleError(err);
console.log(docs);
});
Related
I am new to JS. here is my function, so when I use this, it will return an empty list.
if I replace resultArray.push(result); with console.log(result); it will correctly show me the result.
I am expecting to see the function return the query result inside a list.
function queryMongo (key) {
var url = "mongodb://user:pw/task?replicaSet=bdb";
var resultArray = [];
// connect to the mongoDB
MongoClient.connect(url, { useUnifiedTopology: true }, function(err, db) {
if (err) throw err;
var dbo = db.db("task");
var query = { 'device' : key };
var cursor = dbo.collection('commands').find(query);
cursor.forEach(function(result){
if (err) throw err;
resultArray.push(result);
}, function(){
db.close();
})
});
return resultArray;
}
As shown in here one way would be to get the whole dataset at once:
async function queryMongo(key) {
var url = 'mongodb://user:pw/task?replicaSet=bdb'
// connect to the mongoDB
return new Promise(function (resolve, reject) {
MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err) throw err
var dbo = db.db('task')
var query = { device: key }
// Fetch all results
dbo
.collection('commands')
.find(query)
.toArray(function (err, items) {
if (err) {
return reject(err)
}
resolve(items)
db.close()
})
})
})
}
async function doWork() {
// without "async" keyword it's not possible to "await" the result
console.log(await queryMongo('some-key'))
}
doWork();
Please take following advices into consideration:
Don't connect/disconnect at each function call aside if you're performing queryMongo() once in a while
The reason your code was not working is because you were returning the result before the async call was actually finished. Hence when doing queryMongo() result was straightly []. Indeed, establishing the connection to mongo and performing the query takes "time" and NodeJS will continue performing code execution while this is happening. I would advise to read a bit around the event loop to understand this mechanic a bit better.
I think that the main problem is that all db calls are asynchronous. You can't just return the value, you can pass to a callback or return Promise.
const MongoClient = require('mongodb').MongoClient;
function queryMongo(key, callback) {
const url = 'mongodb://localhost:27017';
MongoClient.connect(url, { useUnifiedTopology: true }, function(err, db) {
if (err) throw err;
const dbo = db.db('tasks');
const query = { device: key };
dbo
.collection('commands')
.find(query)
.toArray((err, doc) => {
if (err) throw err;
db.close();
callback(doc);
});
});
}
queryMongo(12, doc => {
/* do something */
console.log(doc);
});
I have a function named looper2(), which is accepting an array of data, and im trying to get the id of each data from mysql database.All is working fine, but my nodejs is not waiting for the function, when its still represent await.
var noloop = [];
noloop = await looper2(json_array1);
console.log("db id returns",noloop)
console.log("no loop",noloop[0]); //im getting this undefined
function looper2(){
return new Promise(function(resolve, reject) {
// Do async job
for(var i=0;i<json_array1.length;i++){
var sl = "select id from json where name ="+db.escape(json_array1[i]);
db.query(sl, function(err,result) {
if (err) throw err;
console.log("individual id:", result)
noloop.push(result)
});
}
resolve(noloop)
});
}
The problem is that in looper2 function you resolve immediately after the forLoop and don't wait for the db queries to complete. You should instead resolve after the db queries have all completed
function looper2(){
return new Promise(function(resolve, reject) {
// Do async job
for(var i=0;i<json_array1.length;i++){
var sl = "select id from json where name ="+db.escape(json_array1[i]);
db.query(sl, function(err,result) {
if (err) throw err;
console.log("individual id:", result)
noloop.push(result)
if(noloop.length == json_array1.length) {
resolve(noloop)
}
});
}
});
}
You can't use await without async function. So, you have to create a self-executable function to use await like below. And if you are using promise inside a loop there is promise.all function available, you can use to resolve all pending promises.
(async () => {
var noloop = [];
noloop = await looper2(json_array1);
console.log(noloop);
})();
function async looper2(){
const promises = [];
for(var i=0;i<json_array1.length;i++){
var sl = "select id from json where name ="+db.escape(json_array1[i]);
db.query(sl, function(err,result) {
if (err) throw err;
promises.push(new Promise(result));
});
}
return Promise.all(promises)
}
not able to get items. it return [] . but it show correct on console.log(item). i think before my map() runs complete. it print all data. how to solve this issue. i am new in node.
function getBlockUsers() {
return new Promise(function (resolve, reject) {
BlockUser.find({userId:req.user._id}).populate("blockedId").lean().exec(function (err,result) {
if(err){
reject({"msg":"failed to getting block user."})
}else{
var results = [];
result.map(function(item){
Vehicle.findOne({userId:item.blockedId}).lean().exec(function(err,vehicle){
if(vehicle){
item.vehicleId = vehicle._id;
item.vehicleModel = vehicle.model;
}
results.push(item)
console.log(item)
});
});
resolve(results);
}
})
});
}
Because you use an async function in the map function wish is synchronous you need to create an array of promise and use Promise.all before the resolve to wait for all the results.
The code bellow should fix your issue.
function getBlockUsers() {
return new Promise(function (resolve, reject) {
BlockUser.find({userId:req.user._id}).populate("blockedId").lean().exec(function (err,result) {
if(err){
reject({"msg":"failed to getting block user."})
}else{
var results = result.map(function(item){
// don't forget to return in the map function
return new Promise(function (resolve1, reject1) {
Vehicle.findOne({userId:item.blockedId}).lean().exec(function(err,vehicle){
if (err) return reject1(err)
if(vehicle) {
item.vehicleId = vehicle._id;
item.vehicleModel = vehicle.model;
}
resolve1(item)
});
})
});
// here you wait all the promises of results
resolve(Promise.all(results));
}
})
});
}
The problem is you have non-blocking code inside your result.map().
You should try using just one DB query. Then resolve all the items in the exec callback. Otherwise use a promise for the original query.
Vehicle.find({ $in: { userId: result.map( item => item.blockedId) }}).lean().exec( (err, results) => {
// add the vehicle / model ids to each item in results
resolve(results)
})
I'm getting undefined when I trying to print a value to console which is returned by an asynchronous function.
This is happening in my controller function (dumyController.js) which calls a function written in helper (DBHelper.js), for DRY approach, which
asynchronously fetches data from a model function in DBHelperModel.js
dumyController.js
var dbHelpers = require('../helpers/helpers');
exports.dumyControllerFunc = function (req, res) {
var result= dbHelpers.dumyHelperFunc(165);
console.log(result);
};
DBHelper.js
var dbHelp = require('../models/DBHelperModel');
module.exports = {
dumyHelperFunc: function (userId) {
dbHelp.fetchDataFromDB(userId, function (err, rows) {
var res;
if (err) {
return null;
}
else {
res.send(rows.member_code);
}
});
}
};
DBHelperModel.js
var db = require('../db');
var DBHelpers = {
fetchDataFromDB: function (userId, callback) {
var query = `SELECT member_code FROM members where id=?`;
db.query(query, userId, callback);
},
};
module.exports = DBHelpers;
db.js
var mysql = require('mysql');
var connection = mysql.createPool({
host: '127.0.01',
user: 'root',
password: '',
database: 'dumyDB'
});
module.exports = connection;
I know I'm not getting value because of the asynchronous nature of the function but can anyone tell me how to fetch the value with an architecture like given above. I'm new to nodeJS. Thanks!
I think you want a solution about the asynchronous in nodejs.
The promise,that is a universal and popular solution
Use a callback,That is a approach in nodejs's way,anyway,that is native nodejs's asynchronous solution .
Other asysn: Generate and async, that's es6 feature,and the latter is support by nodejs7+
The promise is most recommend,it has a good looks structure if has a asycn chain, The callback looks bad on that.
This is probably what you trying to achieve:
https://repl.it/MA0Z/0
var DBHelpers = {
fetchDataFromDB: function (userId, callback) {
callback(null, 'hello fetchDataFromDB');
},
};
var dbhelp = {
dumyHelperFunc: function (userId, callback) {
DBHelpers.fetchDataFromDB(userId, function (err, rows) {
if (err) {
callback('error bro');
} else {
callback(null, rows);
}
});
}
}
dbhelp.dumyHelperFunc(165, function (error, result) {
console.log(result);
});
How can I get the results of a method that I have defined inside a Mongoose model. I pass a parameter to the method. Here is what I have tried:
mySchema.methods.getStuff = function(id) {
return this.model('coolSchema')
.findOne({ _id : id })
.exec(function (err, data) {
console.log(data); // I can see that the data is retrieved
return data;
})
};
I then try to call this method by doing the following:
var cool = new coolSchema();
cool.getStuff(id, function (err, data) {
console.log(data); // data is not being passed here as expected
});
What am I doing wrong?
Your schema method should have a callback argument, and call it with the results of the query. The way it is written here you are just returning a query. The callback function you pass as 2nd arg to getStuff is never looked at by that method.
Something like this should work out:
mySchema.methods.getStuff = function(id, cb) { // <-- note getStuff takes a callback arg now
return this.model('coolSchema')
.findOne({ _id : id })
.exec(function (err, data) {
if (err) cb(err); // check for errors
console.log(data); // I can see that the data is retrieved
cb(null, data); // <-- call the callback with the results
})
};
I assume you left off for brevity but it's also a good idea to check undefined/null id and that cb is actually callable.
You can also use a promise library to make a promisified version of the model:
var Promise = require('bluebird');
mySchema = Promise.promisify(mongoose.model('mySchema'));
Then in your schema method use the Async versions of the regular methods:
mySchema.methods.getStuff = function(id, cb) {
return this.model('coolSchema')
.findOneAsync({ _id : id }) // <-- using 'promisified' version of method
.then(function (id) {
if (!id) throw new Error('ID wasn't found!')
return id;
})
.catch(function(e){
// db was down, etc. try to handle, or rethrow
console.log('db error');
})
};
then call then:
cool.getStuff(id).then(function (data) {
console.log(data);
})
.catch(function(e){}
console.log('Error: ' + e.message);
);