Async Javascript is blowing my mind - javascript

I have used async/wait a little within functions. And I know about callbacks but for some reason I can't get what i want to work. This is essentially what I want to do.
T.get('friends/ids', myAcc, function(err, data, response){
friends = data;
});
console.log(friends);
what happens is it does the console log before getting the friends.
i know if I do this, it will work
T.get('friends/ids', myAcc, function(err, data, response){
console.log(data);
});
but how can I wait for the function its self to complete? Without surrounding them with another async/aawait function?

You can use Promise constructor
(async() => {
let friends = await new Promise((resolve, reject) => {
T.get('friends/ids', myAcc, function(err, data, response) {
if (err) {
reject(err);
return;
}
resolve(data);
});
}).catch(err => err);
console.log(friends);
})();

Related

UnhandledPromiseRejection when returning promise in async-await function

I've already searched among a lot of questions asked by other users but I can't still figure out why my function doesn't work properly.
I wrote this code:
async function getFile(c, sourcePath, fileName, destPath) {
return await new Promise((resolve, reject) => {
c.cwd(sourcePath, function(err, currentDir) { // "c" is an instance of ftp module's class, "cwd" is one of its methods to change working directory
err = new Error('trying to throw an error for testing the func');
if (err) reject(err);
c.get(fileName, function(err, stream) {
if (err) reject(err);
stream.once('close', function() { resolve(); });
stream.pipe(fs.createWriteStream(destPath));
});
});
}));
}
(async function() {
try {
await getFile(c, '/path/to', 'file.xls', 'file.xls');
} catch (err) {
throw err;
}
})();
Node returns an UnhandledPromiseRejectionWarning, and I don't understand why. I have returned a promise in the first function and then called that function in another one correctly structured with async-await syntax. What is wrong with my code?

Function returning "undefined" while trying to query database in node.js

This piece of code always returns undefined to the calling function. I'm new to JavaScript and I did try and look into some of the callback solutions on Stack Overflow and other resources but none of them worked for me. Any help with this would be highly appreciated. Thank you!
var funcs = require('./index.js');
var connected = require('./database')
query='SELECT * from trades'
const res = execute_rows(query)
console.log(res)
function execute_rows(query){
con = connected();
con.query(query, function(err, result, fields) {
if (err) {
return console.log(err);
}
console.log(result) //Displays correct results
return result; // Returns undefined to calling function
})
con.end();
}
Harshith
Ok, if you print de result of your query inside the function execute_rows just before de return statement you should be able to see it.
The problem with your function is your not actually returning the result "on time".
You need to return a promise to get the result from your function.
This could be a little hard to understand when you're new in JS. Your function should be something like this:
function execute_rows(query){
con = connected();
return new Promise((resolve, reject) => {
con.query(query, function(err, result, fields) {
if (err) {
// Returning the error
reject(err);
con.end();
}
resolve(result);
con.end();
});
});
}
Now, when you call you call your function, you have two options:
Use async/await
Still using promises (callback-driven)
Calling your function:
const dbResult = await execute_rows(query);
or
execute_rows(query).then(result => {
// Get the result
console.log('result:', result);
}).catch(error => {
// Get the error
console.log('error:', error);
});
Please, read about promises and async/await pattern!
Links:
Promises:
https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Promise
Async/await:
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await

From where should I call module.exports to get a not null value?

Where should I call module.export, I assume, it's supposed to be a callback function.
But I'm confused as to where am I supposed to call the callback function.
I'm still confused with the solution, too complicated for me.
sql.connect(config, function(err) {
if (err)
console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('select part_num,qty from CRM.CRM.Fishbowl_Inventory where not location = \'Shipping\'',
function(err, recordset) {
if (err)
console.log(err)
// send records as a response
var details = recordset;
});
});
module.exports = details;
Confusion:
Extremely sorry to bother you guys but I want to be sure that I'm doing no harm to our database by involving any database request through Javascript.
I'm testing directly with our production database, hence cautious
So as Max provided in his answer the following code
const connectToSql = require('./connectToSql');
connectToSql()
.then(details => {
console.log(details);
//Here I can do as much logic as I want
//And it won't affect my database or call multiple requests on my DB
})
.catch(err => {
console.log(err);
});
I can understand I'm asking super silly questions, very sorry about that.
You can't export the result of your function. You want to export a function that will return your value. Like this:
function connectToSql(config) {
return new Promise((resolve, reject) => {
sql.connect(config, function (err) {
if (err) {
console.log(err);
reject(err);
}
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query('select part_num,qty from CRM.CRM.Fishbowl_Inventory where not location = \'Shipping\'',
function (requestErr, recordset) {
if (err) {
console.log(requestErr);
reject(requestErr);
}
resolve(recordset);
});
});
});
}
module.exports = connectToSql;
Because your function is async, I returned a promise that will return your result. Also, your second error from your query is named the same as your first error from the connection. That would cause problems.
Example of how to use this:
const connectToSql = require('./connectToSql');
connectToSql()
.then(details => {
console.log(details);
})
.catch(err => {
console.log(err);
});

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);
});
});
}

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