How to use callback in .then() function in Nodejs? - javascript

I have nodejs module to fetch data from mongodb database using mongodb driver. Callback is passed to given function which return a promise, but instead of returning result in .then() function, it is passing value to callback function. How can I call this function from other module or function since it is not returning it in .then()? I tried to console the result of .then(), but it is showing undefined.
const MongoClient = require('mongodb').MongoClient;
const Db = require('../model/db');
Db.findUser = (details, callback) => {
return dbconnection().then(db => {
if (db) {
return db.collection('users').findOne({
email: details.email,
pass: details.password
}).then(data => {
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
})
}
I have used following function to call the promise. I am new to promises.
var getUser = function(callback) {
db.findUser().then(result => {
console.log(result) // undefined
})
}

You can easily do it using async/await. Something like this:
Db.findUser = async (details, callback) => {
const db = await dbconnection();
const data = await db.collection('users').findOne({
email: details.email,
pass: details.password
});
if (data) {
console.log('Found one');
callback(true);
} else {
let err = new Error();
callback(err);
}
return data;
}
and consume it like:
const getUser = async (details, callback) => {
const data = await Db.findUser();
// do whatever you need with data
return data;
}

Related

Return value from externally called fs.Readfile() in node

I am trying to get a value returned from a function where I read and write a file using fs.readFile/writeFile in Node.
In my main server.js file, a request comes in and I then want to send an email from another file called sendEmail.js:
const fs = require('fs')
const sendMail = require('./sendEmail')
async function sendAnEmail() {
let resultOfSend = await sendMail.sendEmail()
resultOfSend.then((result)=>{
// return the result
}
}
sendAnEmail();
In sendEmail I first read a file to get the email to send to,
then write to a second file
then, if all is good, I send an email (from a separate function):
async function sendEmail() {
// Check if user exists
fs.readFile('./file.json', (err, data) => {
if(err) {
throw error
}
else {
let users = JSON.parse(data)
let dataToWrite = JSON.stringify(users)
fs.writeFile('./file2.json', dataToWrite, (err) => {
if(err) {
console.error(err)
throw error
}
else {
return generateEmail(users)
.then((info) => {
return info
})
.catch(console.log('err'))
}
})
}
})
}
async function generateEmail(user) {
let msgText = 'hello world'
// Set the mail options
const mailOptions = {
...
}
// Send the mail
let info = await transporter.sendMail(mailOptions)
return info
}
module.exports = {sendEmail}
What I can't get is a value for the resultOfSend variable. Keeps coming back undefined, I think because the promise hasn't yet been fulfilled.
How do I get a value to return from the sendEmail function back to the server.js file where it's called from?
You're using await and async in sendEmail but not returning any Promise (So the sendEmail function doesn't return anything and this is why you get undefined).
Nevertheless, on the response you're trying to call .then() even though you used await.
So you should:
return Promise in the sendEmail function.
decide how you want to handle it, if you use async-await then dont use .then() and just analyze the variable and vice versa.
generateEmail() function should also return Promise.
For example:
async function sendEmail() {
return new Promise((resolve, reject) => {
// Check if user exists
fs.readFile('./file.json', (err, data) => {
if(err) {
reject()
}
else {
let users = JSON.parse(data)
let dataToWrite = JSON.stringify(users)
fs.writeFile('./file2.json', dataToWrite, (err) => {
if(err) {
console.error(err)
reject()
}
else {
generateEmail(users)
.then((info) => {
resolve(info)
})
.catch(
console.log('err')
reject()
)
}
})
}
})
})
}

Second function not called asynchronously in nodejs

I am trying to call some function using a single express router , I want to call them in order, meaning that I don't want getLaps() function to execute before get streams function has done all the work , so I tried to use some solutions I found on the internet but it didn't work, the second function doesn't execute. Please help.
Here is my code :
router.get("/", async (req, res,done) => {
res.status(201).send('created user')
return getLaps(function () {
getStreams(function () {
});
});
// await getStreams();
// await getLaps();
// console.log("hey")
});
Here is the get laps function :
function getLaps(req) {
const access_token = '75f2d92fdc445033312854d775e039b6c5bf04e7';
//for test 3756582581,
const idL = [5567017025, 5566531480];
const stravaClient = StravaClientService.getClient(access_token);
const activityService = StravaActivityService(stravaClient);
var params = {
TableName: "run-id",
Key: {
"id": "15428785",
}
};
console.log("cool laps")
docClient.get(params, async function (err, data) {
if (err) {
console.log("Error", err);
} else {
}
idL.map((id, index) => setTimeout(() => activityService.listLaps(id), (5 + index) * 60)
)
//data.Item.json
});
}
and the streams function :
function getStreams(req) {
const idS = [
5567017025, 5566531480
];
const stravaClient = StravaClientService.getClient(access_token);
const activityService = StravaActivityService(stravaClient);
var params = {
TableName: "run-id",
Key: {
"id": "15428785",
}
};
console.log("cool streams")
docClient.get(params, async function (err, data) {
if (err) {
console.log("Error", err);
} else {
idS.map((id, index) => setTimeout(() => activityService.streamActivity(id), (5 + index) * 60))
console.log("got the streams")
}
});
}
in your getStream and getLaps function return promises instead of other object/Stuff like
async function getStream(){
return new Promise(async (resolve, reject){
//Do something
//where you want to return something just call resolve function like
resolve()
//if you want some output of getStream() just pass it to resolve function
//const result = 'I'm result'
resolve(result)
})
}
do same thing with the laps function and in your router call them with await keyword

How to wrap function call inside of Promise

After exporting the required modules and setting up the variables
let AWS = require("aws-sdk");
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const USER_POOL_ID = 'us-east-1_vkXRQuP4U';
const CLIENT_ID = 'mipa4trls0l7323om33mlk80e8';
const poolData = {
UserPoolId : USER_POOL_ID, ClientId : CLIENT_ID
};
const POOL = new AmazonCognitoIdentity.CognitoUserPool(poolData);
let email = "my.email#domain.com";
let password = "My.Password!";
I can go ahead and call signUp command:
POOL.signUp(email, password, [], null, function(err, result) {
console.log('...result:', result);
});
And it works well. Next I want to wrap the POOL.signUp(email, password...) inside async function sign_up like so:
async function sign_up(email, password) {
POOL.signUp(email, password, [], null, function(err, result) {
console.log('...sign_up.result:', result);
return result;
})
};
async function main() {
let signupData = await sign_up(email, password);
console.log('...main.signupData:', signupData);
return signupData;
};
main().then((error, data) => {console.log('...error, data:', error, data)});
While it works fine, the order of the calls that get executed is wrong as the main function doesn't wait for the sign_up() function to complete. In attempt to correct this behavior I wrap the POOL.signUp(email, password...) inside of Promise:
async function sign_up(email, password) {
return await new Promise((resolve) => {
POOL.signUp(email, password, [], null, {
onSuccess: (result) => {
console.log('...result:', result)
return resolve(result);
},
onFailure: (err) => {
return resolve(err.message);
},
});
})
};
But I am getting the error message:
UnhandledPromiseRejectionWarning: TypeError: callback is not a function
Is there a way to avoid this error?
don't need to await the Promise you are returning (since we precisely want our function to be async, we want to defer the waiting to the caller)
Promise constructor function needs to provide the second reject parameter to be able to access the callback in the function implementation
pass a callback function as your POOL.signUp fourth argument, instead of an object
function sign_up(email, password) {
return new Promise((resolve, reject) => {
POOL.signUp(email, password, [], null, function(err, result) {
if (err) {
return reject(err.message);
}
console.log('...result:', result)
resolve(result);
});
})
};

Combine two callbacks into one return

So I have this code:
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
lalamove.getQuotation(data ,context, function(err, llm_data){
callback(null,llm_data)
});
};
So it calls lalamove.getQuotation function and returns an object:
{ "totalFee": "108", "totalFeeCurrency": "PHP" }
Now, I have added a new function, that returns this object:
{ "totalFee": "10", "totalFeeCurrency": "PHP" }
from a different function so I thought I should push them in one array and then that is when I would call the callback but it does not work, this is what I have tried
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
lalamove.getQuotation(data ,context, function(err, llm_data){
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
});
inhouse.getQuotation(data ,context, function(err, ih_data){
const ih_obj = {"inhouse": ih_data }
response.push(ih_obj);
});
callback(null,response);
};
and what I want to be the response is like this:
["lalamove": { "totalFee": "108", "totalFeeCurrency": "PHP" },
"inhouse": { "totalFee": "10", "totalFeeCurrency": "PHP" }]
what am I doing wrong?
Your callback(null,response) will not wait for those two callback functions to finish. You can use Promise and use Promise.all(objs).then(function) to wait for all promises finish and run.
Welcome to World's Javascript world - Callback hell.
We have some options for your case: Callback hell, async lib, Promise, async/await...
Callback hell: Call a async function in a callback
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
lalamove.getQuotation(data, context, function (err, llm_data) {
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
// lalamove.getQuotation done!
// call next action
inhouse.getQuotation(data, context, function (err, ih_data) {
const ih_obj = { "inhouse": ih_data }
response.push(ih_obj);
// inhouse.getQuotation done!
// call the last action
callback(null, response);
});
});
};
Async lib: async
You can use waterfall function to do actions in order, and parallel if order is not matter.
module.exports.getEstimate = (event, context, callback) => {
var data = JSON.parse(event.body);
var response = []
async.parallel([
function (next) {
lalamove.getQuotation(data, context, function (err, llm_data) {
// TODO: check err object
const llm_obj = { "lalamove": llm_data }
response.push(llm_obj);
// lalamove.getQuotation done!
// do next action
next();
});
},
function (next) {
inhouse.getQuotation(data, context, function (err, ih_data) {
const ih_obj = { "inhouse": ih_data }
response.push(ih_obj);
// inhouse.getQuotation done!
// do next action
next()
});
}
], function (err) {
// TODO: check err object
// call the last action
callback(null, response);
});
};
Try wrapping two quotation calls in Promise, then utilise Promise.all to wait for both of them to be completed, then return the result to the callback
module.exports.getEstimate = (event, context, callback) => {
let data = JSON.parse(event.body);
// wrap quotation calls in `Promise`
Promise.all([
new Promise(resolve => lalamove.getQuotation(data, context, (err, lalamove) => resolve({ lalamove }))),
new Promise(resolve => inhouse.getQuotation (data, context, (err, inhouse ) => resolve({ inhouse }))),
]).then(response => {
// return the result back to `callback`
callback(null, response);
})
};
You could also try using util.promisify and the async / await syntax.
For example:
const util = require("util");
module.exports.getEstimate = async (event, context, callback) => {
let data = JSON.parse(event.body);
try {
let response = await Promise.all([ util.promisify(lalamove.getQuotation)(data, context),
util.promisify(inhouse.getQuotation)(data, context) ]);
callback(null, response);
} catch (err) {
callback(err);
}
};
We can also do something similar, but without async / await:
const util = require("util");
const getEstimate = (event, context, callback) => {
let data = JSON.parse(event.body);
Promise.all([util.promisify(lalamove.getQuotation)(data, context),
util.promisify(inhouse.getQuotation)(data, context)])
.then(response => callback(null, response))
.catch(err => callback(err));
};

REST API calls using async await

Here's my code snippet
var clients = require('restify-clients');
async function callApi(val){
const client = clients.createJsonClient({ url: apiUrl });
await client.get('/my/url', (err, req, res, obj) => {
if (err) {
return err;
} else {
return obj;
}
});
}
I've tried a few ways of calling it, but they all aren't working
First way:
var results = await callApi(val);
Second way:
var results = callApi(val).then(data => {
console.log(data);
})
client.get doesn't return a promise, you can't use await on a function which doesn't return a promise (honestly you can, but it doesn't make sense). The correct solution here is to promisify client.get and return a promise:
function callApi(val) {
const client = clients.createJsonClient({ url: apiUrl });
return new Promise((resolve, reject) => {
client.get('/my/url', (err, req, res, obj) => {
if (err) {
reject(err);
} else {
resolve(obj);
}
});
});
}
// Usage
let results = await callApi(val);
Try to remove await from results
var results = callApi(val);

Categories