I'm trying to get my head around callbacks in Node.JS and it's pretty foreign compared to what I'm used to.
I have the following example from another post on here:
var someOutput;
var myCallback = function(data) {
console.log('got data: '+data);
someOutput = data;
};
var usingItNow = function(callback) {
callback('get it?');
};
//now do something with someOutput outside of the functions
I have added the someOutput variable - I want to get the text 'get it?' (obviously I don't) into there so that I can concatenate it on to a string, but it's not working in any combination I've tried.
I must be missing something fundamental here!
Thank you for the help.
You should call the function:
usingItNow(myCallback);
But anyway it is not a good practice to use callback to set variable and consume later. It will work for you now, but later if callback will be async, it will fail.
Consider using Promise and use someOutput in the then function.
A good Promise package is Bluebird
If you want to do it like a pro, consider using Promise.coroutine
http://bluebirdjs.com/docs/api/promise.coroutine.html
Or async + await if you are using an updated version of Node.js
Look on http://node.green/#ES2017-features-async-functions to check what avilable on your node version
As commented by emil:
"call usingItNow(myCallback);"
doh!
Related
I am really struggling here. Admittedly I am no guru especially when it comes to node and asynchronous programming, I am an old C# .net developer so I am comfortable with code but struggling here.
Here's the back story, short and sweet. I have a pg database and I am using the sequelize ORM tools to create a relatively simple CRUD app.
Here's what I want to do.
Make a call to the findAll function on one object.
I need a piece of information from that first call so that I can make a subsequent call.
For instance. Lookup the current user to get their details, grab their ID and now lookup their display preferences.
I know I can run two requests that are not linked using Promise.all, here is an example of this already working.
var delConfig = deliverabiltyConfigs.findAll2(req.signedCookies.tsUser);
var delPack = deliverabilityPackages.findAll2();
Promise.all([delConfig, delPack]).then((results) =>{
res.render('index', { title: 'Deliverability Calculator', UserEmail : req.signedCookies.tsUser, UserName : req.signedCookies.tsUserName, data:results[0], packs:results[1]});
});
Where I am stuck is passing data from one promise to then next and needing them to run asynchronously.
Please help!
There are a few way you can do this. Either use promise chaining or with async & await.
Promise chaining might be the simplest way to do this now, but I would suggest using async await as its easier to read. Since you didn't really provide a sample of what you were trying to do I will make something generic that should hopefully help.
So using promise chaining you would do something like:
pgConnection.findAll().then((data) => {
const foo = data.foo;
pgConnection.findSomething(foo).then((data2) => {
console.log(data2);
});
});
What is happening here is once the promise from findAll() is resolved successfully it will call the .then method and will pass the resulting data there for you to use in your next db query and then I am just printing out the result of the final db query.
This is how you could do it using async & await:
async function getFoo() {
const data = await pgConnection.findAll();
const foo = data.foo;
const data2 = await pgConnection.findSomething(foo);
console.log(data2);
}
The await keyword can only be used inside of an async function so it might not be as simple to change as just using a .then promise chain.
I am trying to run a mongoose query and write it to the console without installing yet another library like bluebird. I have been unable to find this in the documentation.
Here is the query:
function activityH(){
return setRecords.find({'item.title': 'marzipan'}
, 'item.title item.quantity').exec();
}
And the context in which I am calling it:
....[a bunch of promises].then(activityF).then(activityG).then(activityH).then(function(doc){console.log(doc);});
All of the prior activities are completing, but nothing is logging even though my query should have results. I feel this is a very basic question but I have looked for hours trying to find a solution and if this is a duplicate, the original answer is very hard to search for!
Do I absolutely need bluebird to do this? E.g. this blog post
Thank you
You could write a little logging function wrapper to help you out. Something like
function log(data) {
console.log(data);
return data;
}
And then add it your Promise chain.
....[a bunch of promises]
.then(log)
.then(activityF)
.then(log)
.then(activityG)
.then(log)
.then(activityH)
.then(log)
If you want some default messaging you could also pass a message string
function log(msg) {
return function(data) {
console.log(msg, data);
return data;
}
}
And then would add to the chain like:
activityA()
.then(log('activityA'))
.then(activityB)
.then(log('activityB'))
I understand what a callback is and what asynchronous means, what I don't get is how to run asynchronous functions in node.
For example, how is this
var action = (function(data,callback) {
result = data+1;
callback(result);
});
http.createServer(function (req, res) {
action(5, function(r){
res.end(r.toString());
});
}).listen(80);
different from this
var action = (function(data) {
result = data+1;
return result;
});
http.createServer(function (req, res) {
var r = action(5);
res.end(r.toString());
}).listen(80);
?
I guess in the first example I'm doing it asynchronously, yet I don't know how Node knows when to do it sync or async... is it a matter of the return? or the fact that in the sync mode we're doing var x = func(data);?
And also: when to use sync or async? Because obviously you don't want to use it when adding +1... is it OK to use async just when performing IO tasks, such as reading from DB?
For example, I'm using the library crypto to encrypt a short string (50 chars at most), is this case a good example where I should already be using async?
I guess in the first example I'm doing it asynchronously...
Your first example isn't async :) Merely passing a callback and calling it when you're done doesn't make a function asynchronous.
Asynchronous means that, basically, you're telling Node: "here, do this for me, and let me know when you're done while I continue doing other stuff".
Your example is not handing anything to Node for future completion. It's doing a calculation and calling the callback immediately after that. That's functionally the same as your second example, where you return the result of the calculation.
However, you can change your first example to something that is asynchronous:
var action = (function(data,callback) {
setTimeout(function() {
result = data + 1;
callback(result);
}, 1000);
});
Here, you're telling Node to delay calling the callback for one second, by using setTimeout. In the mean time, Node won't get stuck waiting for a second; it will happily accept more HTTP requests, and each one will be delayed one second before the response is sent.
When to use sync or async?
Asynchronous code is "viral": if you rely on functions that are async, your own code that uses those functions will also have to be async (generally by accepting a callback, or using another mechanism to deal with asynchronicity, like promises).
For example, I'm using the library crypto to encrypt a short string (50 chars at most), is this case a good example where I should already be using async?
This depends on which function you're using. AFAIK, most encryption functions in crypto aren't asynchronous, so you can't "make" them asynchronous yourself.
Both examples will work synchronous. Simple async operations are setTimout and setInterval.
Node actually doesn't care what code are you running. You can block or not (blocking/non-blocking).
In other words - you have event loop. If your process is async he will pass the program control to the event loop, so it can execute any other action node needs to be done. If not - he wont.
if you want a function to work asynchronously, you can do that using promises, look at the code below :
function is_asynch(){
return new Promise((resolve,reject)=>{
resolve( here_your_synch_function() )
})
}
I've got a problem with redis and nodejs. I have to loop through a list of phone numbers, and check if this number is present in my redis database. Here is my code :
function getContactList(contacts, callback) {
var contactList = {};
for(var i = 0; i < contacts.length; i++) {
var phoneNumber = contacts[i];
if(utils.isValidNumber(phoneNumber)) {
db.client().get(phoneNumber).then(function(reply) {
console.log("before");
contactList[phoneNumber] = reply;
});
}
}
console.log("after");
callback(contactList);
};
The "after" console log appears before the "before" console log, and the callback always return an empty contactList. This is because requests to redis are asynchronous if I understood well. But the thing is I don't know how to make it works.
How can I do ?
You have two main issues.
Your phoneNumber variable will not be what you want it to be. That can be fixed by changing to a .forEach() or .map() iteration of your array because that will create a local function scope for the current variable.
You have create a way to know when all the async operations are done. There are lots of duplicate questions/answers that show how to do that. You probably want to use Promise.all().
I'd suggest this solution that leverages the promises you already have:
function getContactList(contacts) {
var contactList = {};
return Promise.all(contacts.filter(utils.isValidNumber).map(function(phoneNumber) {
return db.client().get(phoneNumber).then(function(reply) {
// build custom object
constactList[phoneNumber] = reply;
});
})).then(function() {
// make contactList be the resolve value
return contactList;
});
}
getContactList.then(function(contactList) {
// use the contactList here
}, funtion(err) {
// process errors here
});
Here's how this works:
Call contacts.filter(utils.isValidNumber) to filter the array to only valid numbers.
Call .map() to iterate through that filtered array
return db.client().get(phoneNumber) from the .map() callback to create an array of promises.
After getting the data for the phone number, add that data to your custom contactList object (this is essentially a side effect of the .map() loop.
Use Promise.all() on the returned array of promises to know when they are all done.
Make the contactList object we built up be the resolve value of the returned promise.
Then, to call it just use the returned promise with .then() to get the final result. No need to add a callback argument when you already have a promise that you can just return.
The simplest solution may be to use MGET with a list of phone numbers and put the callback in the 'then' section.
You could also put the promises in an array and use Promise.all().
At some point you might want your function to return a promise rather than with callback, just to stay consistent.
Consider refactoring your NodeJS code to use Promises.
Bluebird is an excellent choice: http://bluebirdjs.com/docs/working-with-callbacks.html
you put async code into a for loop (sync operations). So, each iteration of the for loop is not waiting for the db.client(...) function to end.
Take a look at this stackoverflow answer, it explains how to make async loops :
Here
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Waiting for async call
(1 answer)
Closed 8 years ago.
I have a User model function all that returns all users in an array.
User = {
all: function() {
var users = [];
globalLibrary.db.collection('users').find({}).toArray(function(err, items) {
test.equal(null, err);
users = items;
});
return users;
}
}
I want to ensure that the function doesn't finish or return before the mongodb query is complete.
Currently, this function is just returning [], and querying mongodb asynchronously. I want the function to wait until the function finish query is complete and it returns and array filled with users.
Note:
globalLibrary.db is just a cached mongodb connection.
My solution using promises
Since some people closed the question as a duplicate, I'll write my answer here within the question. Hopefully someone else who is not familiar with asynchronous programming find this useful.
Problem
The point is that you need to use a callback - there's no way to block
on something asynchronous. (...) – Aaron Dufour
The function User.all() I wrote above will return empty array because nothing is stopping the process while the mongodb query is happening. There are some primitive ways to stop the process.
You can crank up some hacky stuff using setTimeout(). This way sucks though because you have to use some arbitrary time that might be higher than the actual time you need to query the mongodb. Simply put, it's slower.
You can also use some event based stuff that #AaronDufour linked in the comment (now deleted). So you can have something a pair of event emitter and a listener to replace setTimeout() way. #Someone points out though that you shouldn't use this in node.js for blocking function.
Now finally, the conventional way of dealing with this problem is using callbacks as pointed out by the answer below. This is fine, but callbacks can quickly get out of control once you start having multiple callbacks stacked inside one another.
I am using https://github.com/kriskowal/q for promises. While using promises doesn't solve all the woes of callbacks, but it looks most like simple synchronous programming style which I think is a huge plus.
First do npm install --save q to start using Q package.
Here's my new User.all() function.
var Q = require('q')
var Users = {
all: function() {
var deferred = Q.defer();
globalLibrary.db.collection('users').find({}).toArray(function(err, items) {
if (err) {
deferred.reject(new Error(err));
} else {
deferred.resolve(items);
}
});
return deferred.promise;
}
}
Now if you want to use User.all().
User.all()
.then(function (docs) {
// docs is the array of returned users from mongodb query.
console.log(docs);
}, function(error) {
// do something if there's an error.
}, function(progress) {
// do something while the query is running.
});
The preferred way of doing this in node.js is to embrace the async nature of it and pass in a callback to the function. A few decent tutorials (last one including mongodb examples):
http://justinklemm.com/node-js-async-tutorial/
http://msdn.microsoft.com/en-us/magazine/dn754378.aspx
If you feel you must go against the grain and go synchronous, I'd take a look at this sync library for node.js and mongodb:
https://www.npmjs.com/package/mongo-sync