Redis hGet method goes to deadlock on second call when called asynchronously - node.js

The issue is getMasterData when called never prints 'Passed the worst barrier' :(. God knows what I'm doing wrong. This is what I actually want to achieve.
What am I doing wrong here?
Please let me know. Any help will really be appreciated.
Below is my implementation of node redis implementation:
If I run the same this.getAll() in the loop without any await it works perfectly.
For second time await it never returns back from this.cacheStore.getByKey('organizations') and goes in deadlock forever.
No error and no response either.
Also if I remove data2 await. Still, it works fine.
async setKey(key, data) {
try {
const flatteredData = JSON.stringify(data);
return this.cacheStore.hmset(key, key, flatteredData);
} catch (error) {
return error;
}
}
getByKey(key) { // eslint-disable-line consistent-return
return new Promise((resolve, reject) => {
this.cacheStore.hget(key, key, (err, res) => {
if (err) {
reject(err);
} else {
resolve(JSON.parse(res));
}
});
});
}
async getAll() {
const cache = await this.cacheStore.getByKey('organizations');
if (cache) return cache;
const organizations = await this.db
.collection('organizations')
.find({})
.toArray();
await this.cacheStore.setKey('organizations', organizations);
return organizations;
}
async getMasterData(){
const data1 = await this.getAll();
const data2 = await this.getAll();
console.log('Passed the worst barrier');
}

You declare setKey() as an async function but when you call it in getAll() you forgot to await for it.
Either remove the async or call it with await. I am not sure this will resolve your error but try it out!

Related

NodeJs & mongodb - script does not end

I need my script to end/exit after it is finished and after several tests I thought the problem was my mongodb-class, which connection i never closed. (when I commented out the class btw it's usage, the script ran through and exited like I want it)
But after I have implemented a closing-method, my script still is alive and I don't know why?
this is my mongo-class:
const MongoClient = require('mongodb').MongoClient
class MongodbClient {
constructor(cfg) {
// CONNECT TO MONGO-ENGINE
this.client = new MongoClient(cfg.mongoUrl, { useUnifiedTopology: true });
this.client.connect();
// CONNECT TO DB
this.db = this.client.db(cfg.mongoDbName);
}
// Close connection
async end() {
this.client.close()
return new Promise((resolve, reject) => {
resolve(true)
})
}
// .. some mehtods
}
module.exports = {
MongodbClient: MongodbClient
}
in my main-script I call a function dosomething() at which end the script needs to exit:
parser.dosomething().then(async() => {
await mongo.end()
})
but the sctipt still lives? why is that?
Promise-Ception 😯😯
Your end method returns another Promise within a Promise
async end() {
/* ... */
return true
}
👆 This async function returns a Promise by itself. For async functions it's important to return something at some point.
In your dosomething method you do the correct thing and use await to resolve the Promise.
await mongo.end();
However it doesn't stop there. The first Promise (async end) returns another Promise
// ...
return new Promise((resolve, reject) => {
return resolve(true)
});
// ...
To completely resolve everything, your dosomething method should eventually do this :
const anotherPromise = await mongo.end();
await anotherPromise();
By this time you will realize that client.close() as well returns a Promise and should be resolved. The whole thing is a bit messy IMHO.
Simplify things
Try this
async end() {
try {
await this.client.close();
return true;
} catch (ex) {
throw ex;
}
}
parser.dosomething().then(async () => {
try {
const closed = await mongo.end();
console.log("connection closed");
} catch (ex) {
console.log(ex.message);
}
});
Remember to use try ... catch blocks when using async/await . If the result is still the same 👆 then the problem lies somewhere else probably.
Simplify some more
end() { return this.client.close() }
Now your end method just returns the unresolved Promise from client.close. Please Note, I removed the async prefix from the end method as it is not needed.
await mongo.end();

NodeJS Async Await Undefined Response

I am trying to use async/await to wait for a request to complete and return some information before I proceed with the rest of my code. The function logs the correct response, but the await line says it received an undefined value. This is the function I am calling, which logs the correct response here console.log(loginResponse.idToken);
However, this line let newtoken = await AuthHelper.returnValidToken(token) logs an undefined instead of the response. What mistake am I making here?
returnValidToken: async (token) => {
await AuthHelper.msal
.acquireTokenSilent(loginRequest)
.then((loginResponse) => {
AuthHelper.decodedValidToken(
loginResponse.idToken.rawIdToken,
key,
(jsonToken) => {
if (jsonToken.result === "success") {
// debugger;
console.log(loginResponse.idToken);
return (loginResponse.idToken);
}
}
);
})
.catch((err) => {
console.log(err);
});
},
You should convert the code to use async/await completely
returnValidToken: async (token) => {
try{
const loginResponse = await AuthHelper.msal.acquireTokenSilent(loginRequest);
const jsonToken = await AuthHelper.decodedValidToken(loginResponse.idToken.rawIdToken,key)
if (jsonToken.result === "success") {
console.log(loginResponse.idToken);
return (loginResponse.idToken);
}
}
catch(e){
console.log(e)
return null;
}
},
This is assuming that the AuthHelper.decodedValidToken is also async
As far as I understand this, you should be using either async/await - or Promise.then().
But not both.
It's not that changing the style from a mix of async/await with then didn't work.
The problem is that you're missing a return before AuthHelper.decodedValidToken.
That said, I totally agree that you should choose one style (preferably async/await) and stick to it.

await doesn't wait for function to end

I know this has been asked a lot but I can't seem to use the existing answers to get my code to work. I am trying to use mongojs to make a single query, then put the results in a global (relative to the scope) variable to avoid nesting multiple callbacks. However, I can't seem to get the code to wait for the query to end before continuing.
async function taskmaster() {
const db = mongojs(mongoUri.tasker);
let currentDoc;
const getTask = async function() {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
console.log(doc);
currentDoc = doc;
});
}
await getTask();
console.log(currentDoc);
// Code to process currentDoc below
}
No matter what I do, console.log(doc) shows a valid MongoDB document, yet console.log(currentDoc) shows "undefined". Thanks in advance!
Inside your async function, you use findOne method() in callback style, so it's totally normal that console.log(currentDoc) shows undefined, because it executes before
currentDoc = doc;
You can promisify the findOne method, to use it with async/await keyword.
I found a tutorial to promisfy a callback style function here, hope it help : https://flaviocopes.com/node-promisify/
--- EDIT ---
I rewrite your code when promising the findOne method, as suggested by O.Jones
async function taskmaster() {
const getTask = async (taskId) => {
return new Promise((resolve, reject) => {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
if(err) {
console.log("problem when retrieve data");
reject(err);
} else {
resolve(doc);
}
});
})
const db = mongojs(mongoUri.tasker);
const currentDoc = await getTask(taskId);
console.log(currentDoc);
}

Async - Await issue using Twitter API

I'm currently trying to practice using an API with Twitter API.
I'm using Twit package to connect to twitters API but when I try to do a get request I get
Promise { pending }
I have tried using Async-Await but I'm not sure what I'm doing wrong here.
Here is my code:
const Twit = require('twit');
const twitterAPI = require('../secrets');
//Twit uses OAuth to stablish connection with twitter
let T = new Twit({
consumer_key: twitterAPI.apiKey,
consumer_secret: twitterAPI.apiSecretKey,
access_token: twitterAPI.accessToken,
access_token_secret: twitterAPI.accessTokenSecret
})
const getUsersTweets = async (userName) => {
let params = { screen_name: userName, count: 1 }
const userTweets = await T.get('search/tweets', params, await function (err, data, response) {
if (err) {
return 'There was an Error', err.stack
}
return data
})
return userTweets
}
console.log(getUsersTweets('Rainbow6Game'));
Problem
The biggest assumption that is wrong with the sample code is that T.get is expected to eventually resolve with some data.
const userTweets = await T.get('search/tweets', params, await function (err, data, response) {
if (err) {
return 'There was an Error', err.stack
}
return data // 'data' returned from here is not necessarily going to be received in 'userTweets' variable
})
callback function provided as last argument in T.get function call doesn't have to be preceded by an 'await'.
'data' returned from callback function is not necessarily going to be received in 'userTweets' variable. It totally depends on how T.get is implemented and can not be controlled.
Reason
Thing to be noted here is that async / await works well with Promise returning functions which eventually get resolved with expected data, however, that is not guaranteed here
Relying on the result of asynchronous T.get function call will probably not work because it returns a Promise { pending } object immediately and will get resolved with no data. The best case scenario is that everything with your function will work but getUsersTweets function will return 'undefined'.
Solution
The best solution is to make sure that your getUsersTweets function returns a promise which eventually gets resolved with correct data. Following changes are suggested:
const getUsersTweets = (userName) => {
return new Promise ((resolve, reject) => {
let params = { screen_name: userName, count: 1 }
T.get('search/tweets', params, function (err, data, response) {
if (err) {
reject(err);
}
resolve(data);
})
}
}
The above function is now guaranteed to return expected data and can be used in the following way:
const printTweets = async () => {
const tweets = await getUsersTweet(userName);
console.log(tweets);
}
printTweets();
From what I can see on your code, getUserTweets is an async function, so it will eventually return a promise. I'm assuming you will use this value on another function, so you will need to use it inside an async function and use await, otherwise you will always get a promise.
const logTweets = async (username) => {
try {
const userTweets = await getUsersTweets(username);
// Do something with the tweets
console.log(userTweets);
catch (err) {
// catch any error
console.log(err);
}
}
If logging is all you want and you wrapped it inside a function in which you console.log it, you can call that function directly:
logTweets('someUsername');

NodeJS Async / Await - Build configuration file with API call

I would like to have a configuration file with variables set with data I fetch from an API.
I think I must use async and await features to do so, otherwise my variable would stay undefined.
But I don't know how to integrate this and keep the node exports.myVariable = myData available within an async function ?
Below is the code I tried to write to do so (all in the same file) :
const fetchAPI = function(jsonQuery) {
return new Promise(function (resolve, reject) {
var reqOptions = {
headers: apiHeaders,
json:jsonQuery,
}
request.post(apiURL, function (error, res, body) {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
var wallsData = {}
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
let body = await utils.fetchAPI(jsonQuery)
let pageList = await body[0].dataHashes
for(i=0;i<pageList.length;i++){
var page = pageList[i]
wallsData[page.title.fr] = [page.difficultyList,page.wallType]
}
return wallsData
throw new Error("WOOPS")
}
try{
const wallsData = fetchWalls()
console.log(wallsData)
exports.wallsData = wallsData
}catch(err){
console.log(err)
}
The output of console.log(wallsData) shows Promise { <pending> }, therefore it is not resolved and the configuration file keep being executed without the data in wallsData...
What do I miss ?
Thanks,
Cheers
A promise is a special object that either succeeds with a result or fails with a rejection. The async-await-syntax is syntactic sugar to help to deal with promises.
If you define a function as aync it always will return a promise.
Even a function like that reads like
const foo = async() => {
return "hello";
}
returns a promise of a string, not only a string. And you need to wait until it's been resolved or rejected.
It's analogue to:
const foo = async() => {
return Promise.resolve("Hello");
}
or:
const foo = async() => {
return new Promise(resolve => resolve("Hello"));
}
Your fetchWalls similarly is a promise that will remain pending for a time. You'll have to make sure it either succeeds or fails by setting up the then or catch handlers in your outer scope:
fetchWalls()
.then(console.log)
.catch(console.error);
The outer scope is never async, so you cannot use await there. You can only use await inside other async functions.
I would also not use your try-catch for that outer scope promise handling. I think you are confusing the try-catch approach that is intended to be used within async functions, as there it helps to avoid nesting and reads like synchronous code:
E.g. you could do inside your fetchWalls defintion:
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
try {
let body = await utils.fetchAPI(jsonQuery)
} catch(e) {
// e is the reason of the promise rejection if you want to decide what to do based on it. If you would not catch it, the rejection would chain through to the first error handler.
}
...
}
Can you change the statements like,
try{
const wallsData = fetchWalls();
wallsData.then((result) => {
console.log(result);
});
exports.wallsData = wallsData; // when importing in other file this returns as promise and we should use async/await to handle this.
}catch(err){
console.log(err)
}

Resources