How to use callback or promises in node js? [duplicate] - javascript

This question already has answers here:
How do I convert an existing callback API to promises?
(24 answers)
Closed 6 years ago.
Hello I am new to promises and callbacks in node js
I am doing something to fetch users list using another function using callbacks but getting fail.
somewhere I found to use promises. but never used promises.
can anyone help me with code?
send_noti('12', function(res){
console.log(res);
});
function send_noti(value, callback){
connection.query(" SELECT * from users ", function( err, res ){
callback( res );
});
}

You're looking for something like this:
function sendNotiAsync(value) {
return new Promise((resolve, reject) => // Promise constructor
connection.query("SELECT * from users", (err, data) => // Perform the action
err ? reject(err) : resolve(data))); // If error exists, reject, else resolve
}
And use it like this:
sendNotiAsync(someValue)
.then(data => {
// Work with data here, for example
console.log(data);
})
.catch(err => {
// Handle errors here
console.error(err);
});

In node callbacks of async functions should always have the signature function(err, result /*,...*/)
Your example should look like:
send_noti('12', function(err, res) {
if( !err ) {
console.log(res);
}
});
function send_noti(value, callback) {
connection.query(" SELECT * from users ", callback );
}
Beside that your example does not use Promises. With Promises it would look like this:
send_noti('12')
.then(function(res) {
console.dir(res)
})
.catch(function(err) {
console.dir(err)
})
function send_noti(value, callback) {
return new Promise(function(resolve, reject) {
try {
connection.query(" SELECT * from users ", function(err, res) {
if (err) {
reject(err);
} else {
resolve(res);
}
});
} catch (err) {
reject(err);
}
})
}

Related

Best practices in context of asynchronous Javascript when calling functions in functions?

I am trying to call two functions and pass the output of the first function as a parameter into the second.
Function 1:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err)
throw err;
if(user)
callback(null, user.statistics);
});
}
Function 2:
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err)
throw err;
if(statistics)
callback(null, statistics.game);
});
};
I am trying to execute the second method by passing the output of the first method as a parameter but the asynchronous nature of javascript is messing it up. I have tried implementing promises to no avail.
Can anyone suggest some good javascript practices to deal with calling functions asynchronously when they need each other? Any help would be appreciated.
After fixing the issue I mentioned above, you can call them in sequence like this:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err) callback(err);
if(user) callback(null, user.statistics);
});
};
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err) callback(err);
if(statistics) callback(null, statistics.game);
});
};
someService.getAllStatisticsByUserId(id, (err, statistics) => {
if (err || !statistics) {
// handle error
return;
}
someService.getGameByStatisticsId(statistics.id, (err, game) => {
if (err || !game) {
// handle error
return;
}
// handle game
});
});
However, as noted in Mongoose documentation:
When a callback function is not passed, an instance of Query is returned, which provides a special query builder interface.
A Query has a .then() function, and thus can be used as a promise.
So you can simply rewrite the calls like this:
someService.getAllStatisticsByUserId(id).then(statistics =>
someService.getGameByStatisticsId(statistics.id)
).then(game => {
// handle game
}).catch(err => {
// handle error
});
or convert it into an async/await function:
async function getGameByUserId(id) {
try {
const statistics = await someService.getAllStatisticsByUserId(id);
const game = await someService.getGameByStatisticsId(statistics.id);
// handle game
} catch (error) {
// handle error
}
}
Note that an async function always returns a Promise, so you must await it or chain it with a .then() to ensure completion of the query and resolve the returned value, if any.
It looks like you should be able to write:
getAllStatisticsByUserId("me", (err, stats) => {
getGameByStatisticsId(stats.id, (err, game) => {
console.log(game);
});
});
Here's how it would look if these functions returned promises instead.
getAllStatisticsByUserId("me")
.then(stats => getGameByStatisticsId(stats.id))
.then(game => console.log(game))
Even better, if you're able to use a version of Node that supports async/await then you could write.
let stats = await getAllStatisticsByUserId("me");
let game = await getGameByStatisticsId(stats.id);
console.log(game);
This would mean slightly rewriting the original functions (unless User.findById and Statistics.findById already return promises).
module.exports.getAllStatisticsByUserId = function(id, callback){
return new Promise((resolve, reject) => {
User.findById(id, (err, user) =>{
if(err) return reject(err);
return resolve(user.statistics);
});
});
}

Unhandled Promise Rejection with a .catch() [duplicate]

This question already has answers here:
why settimeout not delay the function execution?
(2 answers)
Closed 5 years ago.
I'm having trouble debugging my first Javascript tool which uses promises. I feel like I am using the .catch() method correctly, as it matches up with other StackOverflow answers to similar questions that have been asked, however I'd still receiving an UnhandledPromiseRejectionWarning for uncaught promises.
My program fetches a list of objects from an S3 and then logs them to console.
Here is the promise chain
s3Helper.setCredentials(program.profile)
.then(s3Helper.findObjects([], null))
.then(data => console.log(data))
.catch(err => utl.error(err));
And here are the two promises
function findObjects (keyArray, token) {
return new Promise((resolve, reject) => {
var S3 = new AWS.S3({apiVersion: '2006-03-01'});
var params = {
Bucket: program.bucket,
Prefix: program.prefix,
Delimiter: program.recursive ? '' : '/',
ContinuationToken: token
};
S3.listObjectsV2(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
function setCredentials (profile) {
// Sets AWS credentials, and rejects if the profile is not found
return new Promise((resolve, reject) => {
AWS.config.credentials = new AWS.SharedIniFileCredentials({profile: profile});
AWS.config.credentials.refresh((err, data) => {
if (err) {
reject(err);
} else if (AWS.config.credentials.accessKeyId) {
resolve();
} else if (AWS.config.credentials.roleArn) {
resolve();
} else {
var error = {
message: `Given profile '${program.profile}' does not exist`
};
reject(error);
}
});
});
}
Sorry if my style or code is bad, I'm still getting used to Javascript!
Your promise chain should look like this:
s3Helper.setCredentials(program.profile)
.then(() => s3Helper.findObjects([], null))
.then(data => console.log(data))
.catch(err => util.error(err));
Note the function: () => ... on the second line
Then .then() method takes a function as it's argument. So you must pass it a function.
Therefore, .then(s3Helper.findObjects([], null)) would only work if s3Helper.findObjects([], null) returns a function. But based on you definition of s3Helper.findObjects([], null), it doesn't. So you need to update your .then() method to .then(() => s3Helper.findObjects([], null)).

Javascript Promise prematurely resolving

I have a function that returns a Promise, that accesses the database and pulls a few lines out, assigning them to a Javascript variable.
The issue is that my '.then' clause is being triggered even though I know the Promise hasn't resolved:
app.post("/api/hashtag", function (req, res) {
FindPopularRumours().then(function (resolveVar) {
console.log(resolveVar);
console.log();
res.send(resolveVar);
}).catch(function () {
console.log("DB Error!");
res.send("DB Error!");
});
});
And the Promise function:
function FindPopularRumours() {
return new Promise((resolve, reject) => {
var hashtags = [];
var dbPromise;
db.collection(HASHTAGS).find().forEach(function (doc) {
hashtags.push(doc.hashtag);
console.log(hashtags);
});
resolve(hashtags);
});
}
The result output is:
[ ]
['#test1']
['#test1', '#test2']
['#test1', '#test2', '#test3']
As you can see, the first line ('[ ]') should ONLY be executed AFTER the hashtags have been output. But for some reason my code seems to think the Promise has been resolved before it actually has.
EDIT1
As per Ankit's suggestion, I have amended my function to:
function FindPopularRumours() {
return new Promise((resolve, reject) => {
var hashtags = [];
db.collection(HASHTAGS).find({}, function (err, doc) {
if (!err) {
doc.forEach(function (arg) {
hashtags.push(arg.hashtag);
console.log(hashtags);
});
resolve(hashtags);
} else {
return reject(err);
}
});
});
}
This still returns the same output response as before (e.g the 'then' clause is running before the promise itself).
My POST function is still the same as before.
The db.collection.find() function is async, so you have to resolve the promise inside the callback for that, something like
function FindPopularRumours() {
return db.collection(HASHTAGS).find().toArray().then( (items) => {
return items.map( doc => doc.hashtag);
});
}
takes advantage of the Mongo toArray() method, that returns a promise directly
Please note that db.collection(HASHTAGS).find() is an asynchronous call. So, your promise is resolved before database query returns. To solve this problem, you need to re-write your database query as follows:
function FindPopularRumours() {
return new Promise((resolve, reject) => {
var hashtags = [];
var dbPromise;
db.collection(HASHTAGS).find({}, function(err, doc){
if(!err){
doc.forEach(function (arg) {
hashtags.push(arg.hashtag);
console.log(hashtags);
});
resolve(hashtags);
}else{
return reject(err);
}
});
});
}
Hope the answer helps you!

How to run multiple asynchronous functions then execute callbacks?

In my Node.js code I need to make 2 or 3 API calls, and each will return some data. After all API calls are complete, I want to collect all the data into a single JSON object to send to the frontend.
I know how to do this using the API callbacks (the next call will happen in the previous call's callback) but this would be slow:
//1st request
request('http://www.example.com', function (err1, res1, body) {
//2nd request
request('http://www.example2.com', function (err2, res2, body2) {
//combine data and do something with it
});
});
I know you could also do something similar and neater with promises, but I think the same concept applies where the next call won't execute until the current one has finished.
Is there a way to call all functions at the same time, but for my final block of code to wait for all API calls to complete and supply data before executing?
Promises give you Promise.all() (this is true for native promises as well as library ones like bluebird's).
Update: Since Node 8, you can use util.promisify() like you would with Bluebird's .promisify()
var requestAsync = util.promisify(request); // const util = require('util')
var urls = ['url1', 'url2'];
Promise.all(urls.map(requestAsync)).then(allData => {
// All data available here in the order of the elements in the array
});
So what you can do (native):
function requestAsync(url) {
return new Promise(function(resolve, reject) {
request(url, function(err, res, body) {
if (err) { return reject(err); }
return resolve([res, body]);
});
});
}
Promise.all([requestAsync('url1'), requestAsync('url2')])
.then(function(allData) {
// All data available here in the order it was called.
});
If you have bluebird, this is even simpler:
var requestAsync = Promise.promisify(request);
var urls = ['url1', 'url2'];
Promise.all(urls.map(requestAsync)).then(allData => {
// All data available here in the order of the elements in the array
});
Sounds like async.parallel() would also do the job if you'd like to use async:
var async = require('async');
async.parallel({
one: function(parallelCb) {
request('http://www.example1.com', function (err, res, body) {
parallelCb(null, {err: err, res: res, body: body});
});
},
two: function(parallelCb) {
request('http://www.example2.com', function (err, res, body) {
parallelCb(null, {err: err, res: res, body: body});
});
},
three: function(parallelCb) {
request('http://www.example3.com', function (err, res, body) {
parallelCb(null, {err: err, res: res, body: body});
});
}
}, function(err, results) {
// results will have the results of all 3
console.log(results.one);
console.log(results.two);
console.log(results.three);
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Promise.all is now included with ES6 so you don't need any 3rd party libraries at all.
"Promise.all waits for all fulfillments (or the first rejection)"
I've setup a gist to demonstrate Promise.all() with refactoring itterations at: https://gist.github.com/rainabba/21bf3b741c6f9857d741b69ba8ad78b1
I'm using an IIFE (Immediately Invoked Function Expression). If you're not familiar, you'll want to be for the example below though the gist shows how with using an IIFE. https://en.wikipedia.org/wiki/Immediately-invoked_function_expression
TL;DR
( function( promises ){
return new Promise( ( resolve, reject ) => {
Promise.all( promises )
.then( values => {
console.log("resolved all promises")
console.dir( values );
resolve( values.reduce( (sum,value) => { return sum+value }) ); //Use Array.prototype.reduce() to sum the values in the array
})
.catch( err => {
console.dir( err );
throw err;
});
});
})([
new Promise( ( resolve, reject ) => {
console.log("resolving 1");
resolve( 1 );
}),
new Promise( ( resolve, reject ) => {
console.log("resolving 2");
resolve( 2 );
})
]).then( sum => { console.dir( { sum: sum } ) } )
I had a similar use case where I had to do 10 concurrent calls. I did it with the combination of async/await and Promise.all.
async function getData() {
try {
let result = null
const ids = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
let promises = ids.map(async (id) => {
return fetch(
`https://jsonplaceholder.typicode.com/todos/${id}`
).then((data) => data.json());
});
result = await Promise.all(promises)
return result
} catch(err) {
console.log("error: ", err)
}
}
getData().then(data => console.log(data))

Using Promise.all() for multiple http/oauth queries

I'm trying to wait for the output of two OAuth calls to an API, and I'm having trouble retrieving the data from those calls. If I use Promise.all(call1,call2).then() I am getting information about the request object.
First, here's the setup for the fitbit_oauth object:
var fitbit_oauth = new OAuth.OAuth(
'https://api.fitbit.com/oauth/request_token',
'https://api.fitbit.com/oauth/access_token',
config.fitbitClientKey,
config.fitbitClientSecret,
'1.0',
null,
'HMAC-SHA1'
);
foodpath = 'https://api.fitbit.com/1/user/-/foods/log/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json';
activitypath = 'https://api.fitbit.com/1/user/-/activities/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json';
Promise.all([fitbit_oauth.get(foodpath, user.accessToken, user.accessSecret),
fitbit_oauth.get(activitypath, user.accessToken,
user.accessSecret)])
.then(function(arrayOfResults) {
console.log(arrayOfResults);
}
I want arrayOfResults to give me the data from the calls, not information about the requests. What am I doing wrong here? I'm new to promises so I'm sure this is easy for someone who isn't.
The callback for a single fitbit_oauth call is as follows:
fitbit_oauth.get(
'https://api.fitbit.com/1/user/-/activities/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json',
user.accessToken,
user.accessSecret,
function (err, data, res) {
if (err) {
console.error("Error fetching activity data. ", err);
callback(err);
return;
}
data = JSON.parse(data);
console.log("Fitbit Get Activities", data);
// Update (and return) the user
User.findOneAndUpdate(
{
encodedId: user.encodedId
},
{
stepsToday: data.summary.steps,
stepsGoal: data.goals.steps
},
null,
function(err, user) {
if (err) {
console.error("Error updating user activity.", err);
}
callback(err, user);
}
);
}
);
Thanks to jfriend00 I got this working, here's the new code:
function fitbit_oauth_getP(path, accessToken, accessSecret) {
return new Promise (function(resolve, reject) {
fitbit_oauth.get(path, accessToken, accessSecret, function(err, data, res) {
if (err) {
reject(err);
} else {
resolve(data);
}
}
)
})};
Promise.all([fitbit_oauth_getP(foodpath, user.accessToken, user.accessSecret),
fitbit_oauth_getP(activitypath, user.accessToken, user.accessSecret)])
.then(function(arrayOfResults) {
console.log(arrayOfResults);
});
Promise.all() only works properly with asynchronous functions when those functions return a promise and when the result of that async operation becomes the resolved (or rejected) value of the promise.
There is no magic in Promise.all() that could somehow know when the fitbit functions are done if they don't return a promise.
You can still use Promise.all(), but you need to "promisify" the fitbit functions which is a small wrapper around them that turns their normal callback approach into returning a promise that was then resolved or rejected based on the callback result.
Some references on creating a promisified wrapper:
Wrapping Callback Funtions
How to promisify?
If you have an async function that accepts a callback to provide the async result such as fs.rename(oldPath, newPath, callback), then you can "promisify" it like this:
function renameP(oldPath, newPath) {
return new Promise(function(resolve, reject) {
fs.rename(oldPath, newPath, function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
renameP("orig.txt", "backup.txt").then(function() {
// successful here
}, function(err) {
// error here
});
Some promise libraries such as Bluebird have a built in .promisify() method that will do this for you (it will return a function stub that can be called on any function that follows the node.js async calling convention).

Categories

Resources