I have a set of values in a mongoDB I need to only be read ones. So when I have read them I delete that line from the DB. But since node is async if I do getValue() twice in quick succession I get the same value, since the DB has not had time to delete the old one. Does anyone know a good way to fix this problem. I can think of a couple but nothing good.
I don’t have my code here so just wrote a quick example to show my problem.
Var getValue = function() {
ReadfromDB(function(data){
deleteRecord(); // show that what we read has been updated
});
}
You could try something like this. The reading is placed into a promise that's done when the deleting is done as well. This can stack so if you do getValue() 20 times it still should execute in the correct order.
var getValue = function(){
var currentPromise;
var read = function(){
return ReadfromDB().then(function(data){
deleteRecord(); // show that what we read has been updated
});
}
return function() {
if(currentPromise){
currentPromise = currentPromise.then(read)
}else{
currentPromise = read();
}
}
}
What you need to do outside this code is make sure ReadfromDB returns a promise object.
Sounds like a good use case for a closure!
var getValue = (function() {
var called = false;
return function() {
if (!called) {
called = true;
ReadFromDB(function(data) {
deleteRecord();
});
}
};
}());
You could also use once to do exactly the same thing.
var once = require('once');
var getValue = once(function() {
ReadFromDB(function(data) {
deleteRecord();
});
});
Related
Little info, i have an arp.js file which takes a subnet address "192.168.2" and gets all strings returned from arp -a and stores in an array.
I can't figure out why my arpList function is returning an undefined value in my index.js file.
All the console.logs are returning the correct values in the arp.js page when called from the index.js, but the ipObj is coming up undefined. Even the console.log before i return of ipObj works.
Any help would be greatly appreciated.
var { spawn } = require('child_process');
const arpLs = spawn('arp', ['-a']);
var bufferData;
module.exports = {
arpList: function (subnet) {
arpLs.stdout.on('data', data => {
bufferData += data
})
arpLs.stderr.on('data', data => {
console.log('error: ' + data);
});
arpLs.on('exit', function (code) {
if (code != 0) {
console.log("Error exiting"); //if error occurs
}
console.log("exit start 1"); // checking internal processes at stages
var dataArray = bufferData.split(' ');
var ipArray = [];
for (i = 0; i < dataArray.length; i++) {
if (dataArray[i].includes(subnet)) {
ipArray.push(dataArray[i]);
console.log("loop working");
}
}
var ipObj = { "lanIps": ipArray };
console.log("Object is there: "+ipObj)
return ipObj; // this obj should be returned to the index.js call using
})
},
sayMyName: function () {
return "Hello";
}
}
//arpList(ipSubnet);
//INDEX.js
//the index page looks like this
//var arp = require('./arp.js);
//var ipSubnet = "192.168.2";
//var lanIps = arp.arpList(ipSubnet);
//console.log(lanIps);
I ended up adding a callback function to arpList - function (subnet, callback)
Then instead of returning the value pass it into the callback
Then on the index.js side instead of
var lanIps = arp.arpList(value)
i used
arp.arpList(value, function(res){lanIps = res}
return ipObj; // this obj should be returned to the index.js call using
It won't be returned. The reference say nothing about return value. Node-style callbacks rarely work like that because they are potentially asynchronous and returned value cannot be taken into account.
This a special case of this well-known problem. The process is asynchronous and is finished after arp.arpList(ipSubnet) call, there's nothing to assign to lanIps. This is a use case for promises. There are already third-party promisified counterparts like child-process-promise.
The problem can be also solved by moving to synchronous API. child_process functions have synchronous counterparts, including spawnSync.
I am new to node.js and I thought I was beginning to understand asynchronous functions, but this code made me think that I did not understand it correctly anyway.
I am preparing for an insert to mongoDB with mongoose, and the object to insert is post. What made me wonder is that not always post.kunde or post.leverandor is set before the insert.
I thought that as long there is no asynchronous function, the code should execute line by line.
function create_post(account, dato, fakturanummer, bilag, bilagstype, supplier, customer,
descr, moms, amount, saldo, comId ) {
return new Promise(function(resolve, reject) {
var post = new Posteringer;
post.konto = account;
post.dato = dato;
post.fakturanummer = fakturanummer;
post.bilag = bilag;
post.bilagstype = bilagstype;
if (Object.keys(supplier).length) {
post.leverandor = supplier;
}
if (Object.keys(customer).length) {
post.kunde = customer;
}
post.tekst = descr;
post.moms = moms;
post.belob = amount;
post.saldo = saldo;
post.companyId = comId;
var promise = Accounts.findSingle(comId, account).exec();
promise.then(function(acc) {
post.navn = acc.Navn;
console.log(post);
post.save(function(err) {
if (err) {console.log(err.message);}
resolve(true);
});
});
});
}
So there are a few things wrong here. A promise is basically saying "hey, I'm not done doing my stuff yet, but I'll promise to get this to you when I figure out all the things I need to do."
I am not entirely sure how you are calling this, but think of this like a big callback function. So after this you'd have something like,
create_post(......).then(
function(post){
post.save(function(err){
if (err) {console.log(err.message);}
});
});
The real issue I see is you are only resolving one promise. I am not 100% sure if you have to resolve the Mongo promise, unless you are using bluebird promises with it. Your first promise never gets returned though.
I will redo your code, I think this should work:
function create_post(account, dato, fakturanummer, bilag, bilagstype, supplier, customer,
descr, moms, amount, saldo, comId ) {
return new Promise(function(resolve, reject) {
var post = new Posteringer;
post.konto = account;
post.dato = dato;
post.fakturanummer = fakturanummer;
post.bilag = bilag;
post.bilagstype = bilagstype;
if (Object.keys(supplier).length) {
post.leverandor = supplier;
}
if (Object.keys(customer).length) {
post.kunde = customer;
}
post.tekst = descr;
post.moms = moms;
post.belob = amount;
post.saldo = saldo;
post.companyId = comId;
var promise = Accounts.findSingle(comId, account).exec();
promise.then(function(acc) {
post.navn = acc.Navn;
console.log(post);
});
return (err ? reject(err) : resolve(post));
});
}
Then when you call this function call it like in my first example!
create_post(......).then(
function(post){
post.save(function(err){
if (err) {console.log(err.message);}
});
});
Literally what you are saying is if this resolves and I get my data without any errors, send back post. Then pass post into my callback function and do whatever you want to do with it.
Edit:
Make sure you always return something from your promises, as far as I know the async call will never resolve as it never receives anything. Although someone might have an example where this isn't true.
Edit: I know JS is asynchronous, I have looked over the How to Return thread. The issue I'm having is that going from "foo" examples to something specific = I'm not quite sure where to re-format this.
Also here is some context: https://github.com/sharkwheels/beanballs/blob/master/bean-to-osc-two.js
I have a question about returns in node. It might be a dumb question, but here goes. I have a function that connects to a socket, and gets OSC messages from processing:
var sock = dgram.createSocket("udp4", function(msg, rinfo) {
try {
// get at all that info being sent out from Processing.
//console.log(osc.fromBuffer(msg));
var getMsg = osc.fromBuffer(msg);
var isMsg = getMsg.args[0].value;
var isName = getMsg.args[1].value;
var isAdd = getMsg.address;
var isType = getMsg.oscType;
// make an array out of it
var isAll = [];
isAll.push(isName);
isAll.push(isMsg);
isAll.push(isAdd);
isAll.push(isType);
// return the array
console.log(isAll);
return isAll;
} catch (error) {
console.log(error);
}
});
Below I have the start of another function, to write some of that array to a BLE device. It needs name and characteristics from a different function. How do I get the below function to use isAll AND two existing parameters?
var writeToChars = function (name, characteristics) { // this is passing values from the BLE setup function
// i need to get isAll to here.
// eventually this will write some values from isAll into a scratch bank.
}
Thanks.
async call in this case be written something like this. state can be maintained in the variables in closure if required. In this particular case - you can do without any state (isAll) as well.
var isAll;
var soc = dgram.createSocket('udp4', oncreatesocket);
function oncreatesocket(msg, rinfo)
{
isAll = parseMessage(msg);
writeData(isAll);
}
function parseMessage(msg) {
...
// code to parse msg and return isAll
}
function writeData() {}
if the writeData is small enough function. It can be inside oncreatesocket without impacting the readability of the code.
Alright. So I figured out what to do, at least in this scenario. I'm sure there is a better way to do this, but for now, this works.
I'm mapping an existing global array of peripherals into the write function, while passing the OSC message to it as a parameter. This solved my issue of "how do I get two pieces of information to the same place". It figures out which peripheral is which and writes a different value to each scratch bank of each peripheral accordingly. Leaving here for future reference.
var writeToBean = function(passThrough){
var passThrough = passThrough;
console.log("in Write to bean: ", passThrough);
_.map(beanArray, function(n){
if(n.advertisement.localName === passThrough.name){
//var name = n.advertisement.localName;
n.discoverSomeServicesAndCharacteristics(['a495ff20c5b14b44b5121370f02d74de'], [scratchThr], function(error, services, characteristics){
var service = services[0];
var characteristic = characteristics[0];
var toSend = passThrough.msg;
console.log("service", service);
console.log("characteristic", characteristic);
if (toSend != null) {
characteristic.write(new Buffer([toSend]), false, function(error) {
if (error) { console.log(error); }
console.log("wrote " + toSend + " to scratch bank 3");
});
}
// not sure how to make the program resume, it stops here. No error, just stops processing.
});
}
});
}
I'm using this wonderful sync module, synchronize.js - http://alexeypetrushin.github.io/synchronize/docs/index.html.
I've run into a situation where I have to get the return value of the sync'd function into the scope outside of the fiber. Here's a basic example of what I'm talking about:
var records = sync.fiber(function() {
var results = ... // some synchronized function
return results;
});
Whereas records would, in theory, contain the value of resultsfrom within the fiber scope. I've been reading up on futures (fibers/futures module) and how they might be used in this situation but I have yet to come up with anything close to working. I'd love some direction and/or a solution.
edit:
For a more thorough example of what I'm looking to accomplish:
// executes a stored procedure/function
exec: function (statement, parameters) {
init();
var request = new sql.Request(),
results;
processParams(parameters, request);
var res = sync.fiber(function(){
try {
var result = sync.await(request.execute(statement, sync.defers('recordsets', 'returnValue')));
results = result.recordsets.length > 0 ? result.recordsets[0] : [];
return results;
}
catch (e) {
console.log('error:connection:exec(): ' + e);
throw(e);
}
});
// though typical scope rules would mean that `results` has a
// value here, it's actually undefined.
// in theory, `res` would contain the return value from the `sync.fiber` callback
// which is our result set.
return res;
}
As you can see here, what I'd like to accomplish is to get the value of results in the primary scope, from the fiber's scope.
Now it does support it, use following form
var records = sync.fiber(function() {
var results = ... // some synchronized function
return results;
}, function(err, results){... /* do something with results */});
It's not a scope problem. This wont work because return res; executes before the fiber returns. That is why it's undefined.
You need to rewrite your exec function to take a callback. Then you could use synchronize.js on the exec function itself.
I am trying to wrap the node-memcached api with deferred's promisify in order to simplify my nested callbacks.
When I try to call the promisified function I just get "TypeError: Cannot read property 'namespace' of undefined".
Memcached = require('memcached');
var memcache = new Memcached('localhost:11211');
var add = deferred.promisify(memcache.add);
add('myKey', 'myVal', 0)(function(result) {
...
});
I can't seem to find anyone else trying to wrap node-memcached, or getting my same error. Any insight into what may be going wrong? Or maybe even a push into a better direction if this is imperfect?
Thanks!
EDIT::
Just wanted to response that I found the best solution I could for now by doing some digging.
It seems that deferred.promisify calls the passed function with its own scope (this), instead of the context of the function that is passed in.
Using my own promisfy function appears to fix the issue (idea from http://howtonode.org/promises):
function promisify(fn, context) {
return function() {
var def = deferred();
var args = Array.prototype.slice.call(arguments);
args.push(function(err, val) {
if (err !== null) {
return def.reject(new Error(err));
}
return def.resolve(val);
});
fn.apply(context, args);
return def.promise;
};
}
When promisify instances members you should bind it to this instance like:
Memcached = require('memcached');
var memcache = new Memcached('localhost:11211');
var add = deferred.promisify(memcache.add.bind( memcache ) );
add('myKey', 'myVal', 0)(function(result) {
...
});