Nodejs promise pending - javascript

I'm trying to make an constructor for multiple Redis connections, so i've started to try something.
I'm only getting back from has Promise { }, but if I do an console.log before the return I'm getting the real Value.
EDIT: Tried without async/await still don't work.
app.js
const rBredis = require("./redis");
const redis = new rBredis();
console.log(redis.has("kek"));
redis.js
const Redis = require("ioredis");
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
async has(id) {
return await this.redis.exists(id)
.then( exists => {
// console.log(exists); works 0
return exists; // works not Promise { <pending> }
});
};
}
module.exports = BasicRedis;

I don't understand your question completely but I see a problem here.
You need to brush up your knowledge of Promises and Async await. You either use async
await or Promises (.then) syntax to make it work properly.
redis.js
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
// You can either do it like this
has(id) {
return new Promise((res, rej) => {
this.redis.exists(id)
.then( exists => {
res(exists)
}).catch(err => {
rej(err.message)
});
})
};
// Or like this
has(id) {
return this.redis.exists(id)
};
}
In both cases, you can await/.then result in your app.js
// app.js
const rBredis = require("./redis");
const redis = new rBredis();
redis.has("kek").then(res => console.log(res))
EDIT - 1
If this is something that'd take time even 1 millisecond there's no way you're going to get the value right away. You need to use either async-await or promises. Or use a callback like this
redis.js
class BasicRedis {
constructor() {
// TODO
};
redis = new Redis();
has(id, callback) {
this.redis.exists(id)
.then( exists => {
callback(exists)
}).catch(err => {
callback(err.message)
});
};
}
app.js
const rBredis = require("./redis");
const redis = new rBredis();
redis.has("kek", (res) => console.log(res))
Here's reference to Promises MDN and Async Await MDN
Hope it helps.

Related

How do I access promise callback value outside of the function?

It is to my understanding that callback functions are asynchronous and cannot return a value like regular functions. Upon reading about promises, I thought I grasped a good understanding about them, that they are basically an enhanced version of callbacks that allows returning a value like asynchronous function. In my getConnections method, I am attempting to call the find() function on my database through mongoose, and I am attempting to grab this array of objects and send it to the views.
var test = new Promise((resolve, reject) => {
Database.find().then(list => {
resolve(list);
}).catch(err=> {
return reject(err);
})
})
console.log(test)
When I attempt to log to the console outside of the promise function, I get Promise { _U: 0, _V: 0, _W: null, _X: null }
I don't think this is functioning correctly, and I thought I utilized promises correctly. Could anyone point me in the right direction on how to return this array of objects outside of the callback function?
You can simply add await before the promise declaration.
i.e.
var test = await new Promise...
The thing is that when you write a function like this:
const getData = async () => { const response = await fetch(someUrl, {}, {}); return response;}
Then you also need to await that function when you call it. Like this:
const setData = async () => { const dataToSet = await getData(); }
let test = new Promise((resolve, reject) => {
Database.find().then(list => {
resolve(list);
}).catch(err=> {
return reject(err);
})
})
test
.then(result=>console.log(result))
Should solve your problem.
var someValue;
var test = await new Promise((resolve, reject) => {
Database.find().then(list => {
resolve(list);
}).catch(err=> {
return reject(err);
})
}).then(res => {
someValue=res;
})
console.log(someValue);

How to convert promise object to object in class

i want to fetch data from outside of my project with axios. i do it in side of class but for some reason i retrieve data in promise object i use await and promise but eventually i receive data in [object promise].
const Online_Visitors_System = class OnlineVisitors {
constructor() {
// get VisitorIP
this.IP = this.fetchIP();
// config redis for key space notification
this.redis = Redis.createClient();
this.redis.on("ready", () => {
this.redis.config("SET", "notify-keyspace-events", "KEA");
});
PubSub.subscribe("__keyevent#0__:incrby");
}
async fetchIP() {
return new Promise((resolve, reject) => {
return axios
.get("https://api.ipgeolocation.io/getip")
.then(res => resolve(res.data.ip));
});
}
VisitorInter() {
console.log(this.IP);
}
};
module.exports = new Online_Visitors_System();
error that i encounter with it::
This is converted to "[object Promise]" by using .toString() now and will return an error from v.3.0
on.
Please handle this in your code to make sure everything works as you intended it to.
Promise { '51.38.89.159' }
Well you missed await in fews places, here is full correction:
const Online_Visitors_System = class OnlineVisitors{
constructor(){
// get VisitorIP
this.fetchIP().then(ip => this.IP = ip);
// config redis for key space notification
this.redis = Redis.createClient();
this.redis.on('ready',()=>{
this.redis.config('SET',"notify-keyspace-events",'KEA')
})
PubSub.subscribe("__keyevent#0__:incrby")
}
fetchIP(){
return new Promise((resolve,reject)=>{
axios.get('https://api.ipgeolocation.io/getip')
.then(res=>resolve(res.data.ip))
})
}
VisitorInter(){
console.log(this.IP)
}
};
Since the method fetchIP is an async function you need also await when calling it,
so: this.IP = await this.fetchIP().
But since you are in construcot you can't use await so the solution is to use chaning:
this.fetchIP().then(ip => this.IP = ip);
Note that when initating new Promise you need to give it an async function, because inside that you are awating other methods.
You are assigning the promise of an IP address into this.IP.
You will need to .then the promise to get the actual IP address; it might or might not be available by the time VisitorInter() or anything else that needs the IP address is called.
class OnlineVisitors {
constructor() {
this.ipPromise = this.fetchIP();
// redis stuff elided from example
}
async fetchIP() {
const resp = await axios.get("https://api.ipgeolocation.io/getip");
return resp.data.ip;
}
async VisitorInter() {
const ip = await this.ipPromise; // this could potentially hang forever if ipgeolocation.io doesn't feel like answering
console.log(ip);
}
};
module.exports = new OnlineVisitors();

properly using async and await

The function below calls several asynchronous functions in a for loop. It's parsing different CSV files to build a single JavaScript object. I'd like to return the object after the for loop is done. Its returning the empty object right away while it does the asynchronous tasks. Makes sense, however I have tried various Promise / async /await combinations hopes of running something once the for loop has completed. I am clearly not understanding what is going on. Is there a better pattern to follow for something like this or am I thinking about it incorrectly?
async function createFormConfig(files: string[]): Promise<object>
return new Promise(resolve => {
const retConfig: any = {};
for (const file of files) {
file.match(matchFilesForFormConfigMap.get('FIELD')) ?
parseCsv(file).then(parsedData => {
retConfig.fields = parsedData.data;
})
: file.match(matchFilesForFormConfigMap.get('FORM'))
? parseCsv(file).then(parsedData => retConfig.formProperties = parsedData.data[0])
: file.match(matchFilesForFormConfigMap.get('PDF'))
? parseCsv(file).then(parsedData => retConfig.jsPdfProperties = parsedData.data[0])
: file.match(matchFilesForFormConfigMap.get('META'))
? parseCsv(file).then(parsedData => {
retConfig.name = parsedData.data[0].name;
retConfig.imgType = parsedData.data[0].imgType;
// console.log(retConfig); <- THIS CONSOLE WILL OUTPUT RETCONFIG LOOKING LIKE I WANT IT
})
: file.match(matchFilesForFormConfigMap.get('PAGES'))
? parseCsv(file).then(parsedData => retConfig.pages = parsedData.data)
: console.log('there is an extra file: ' + file);
}
resolve(retConfig); // <- THIS RETURNS: {}
});
This is the code I'm using to call the function in hopes of getting my 'retConfig' filled with the CSV data.
getFilesFromDirectory(`${clOptions.directory}/**/*.csv`)
.then(async (files) => {
const config = await createFormConfig(files);
console.log(config);
})
.catch(err => console.error(err));
};
First, an async function returns a Promise, so you dont have to return one explicitely.Here is how you can simplify your code:
async function createFormConfig(files: string[]): Promise<object> {
// return new Promise(resolve => { <-- remove
const retConfig: any = {};
// ...
// The value returned by an async function is the one you get
// in the callback passed to the function `.then`
return retConfig;
// }); <-- remove
}
Then, your function createFormConfig returns the config before it has finished to compute it. Here is how you can have it computed before returning it:
async function createFormConfig(files: string[]): Promise<object> {
const retConfig: any = {};
// Return a Promise for each file that have to be parsed
const parsingCsv = files.map(async file => {
if (file.match(matchFilesForFormConfigMap.get('FIELD'))) {
const { data } = await parseCsv(file);
retConfig.fields = data;
} else if (file.match(matchFilesForFormConfigMap.get('FORM'))) {
const { data } = await parseCsv(file);
retConfig.formProperties = data[0];
} else if (file.match(matchFilesForFormConfigMap.get('PDF'))) {
const { data } = await parseCsv(file);
retConfig.jsPdfProperties = data[0];
} else if (file.match(matchFilesForFormConfigMap.get('META'))) {
const { data } = await parseCsv(file);
retConfig.name = data[0].name;
retConfig.imgType = data[0].imgType;
} else if (file.match(matchFilesForFormConfigMap.get('PAGES'))) {
const { data } = await parseCsv(file);
retConfig.pages = data;
} else {
console.log('there is an extra file: ' + file);
}
});
// Wait for the Promises to resolve
await Promise.all(parsingCsv)
return retConfig;
}
async functions already return promises, you don't need to wrap the code in a new one. Just return a value from the function and the caller will receive a promise that resolves to the returned value.
Also, you have made an async function, but you're not actually using await anywhere. So the for loop runs through the whole loop before any of your promises resolve. This is why none of the data is making it into your object.
It will really simplify your code to only use await and get rid of the then() calls. For example you can do this:
async function createFormConfig(files: string[]): Promise<object> {
const retConfig: any = {};
for (const file of files) {
if (file.match(matchFilesForFormConfigMap.get('FIELD')){
// no need for the then here
let parsedData = await parseCsv(file)
retConfig.field = parsedData.data
}
// ...etc
At the end you can just return the value:
return retConfig

Get data using await async without try catch

I am trying to use await-async without try-catch for this:
const getUsers = async (reject, time) => (
new Promise((resolve, reject) => {
setTimeout(() => {
if (reject) {
reject(....)
}
resolve(.....);
}, time);
})
);
module.exports = {
getUsers ,
};
With try-catch block it looks like this:
const { getUsers } = require('./users');
const users = async () => {
try {
const value = await getUsers(1000, false);
.....
} catch (error) {
.....
}
}
users();
How can I write the same code without using the try-catch block?
Using the promise functions then-catch to make the process simpler I use this utils :
// utils.js
const utils = promise => (
promise
.then(data => ({ data, error: null }))
.catch(error => ({ error, data: null }))
);
module.exports = utils;
And then
const { getUsers } = require('./api');
const utils = require('./utils');
const users = async () => {
const { error, data } = await utils(getUsers(2000, false));
if (!error) {
console.info(data);
return;
}
console.error(error);
}
users();
Without using the try-catch block I got the same output, this way makes it better to understand the code.
In Extension to L Y E S - C H I O U K H's Answer:
The Utils Function is actually correct but, make sure to add the return keyword before the promise as shown down below:
// utils.js
const utils = promise => (
return promise
.then(data => { [data, null]; })
.catch(error => { [null, error]; });
);
module.exports = utils;
When Calling in Main Code:
let resonse, error; // any variable name is fine make sure there is one for error and the response
[response, error] = await utils(any_function()); // Make sure that inside the tuple, response is first and error is last like: [response, error].
if (error) console.log(error);
// -- Do Whatever with the Response -- //
Using My Method Would Give you Benefits like:
Your Own Variable Names.
Not Running into Type Safety issues when using Typescript.
Good Reason to Strong Type your code.
Personally, I have been using this in my code lately, and has reduced some many headaches, my code is cleaner, I don't have to stick with the same variable names, especially when working on a large codebase.
Happy Coding :)
See Ya!
If you have a valid default for the error case you can use the catch method on the getUsers promise and then await a promise whose error will be handled
const users = async () => {
const value = await getUsers(1000, false).catch(e => null);
}
While this approach should work it should be noted that this may mask the case when getUsers returns null vs when it raises an error, and you will still need to check for the null or get a null access error. All in all I would stick with the try { .. } catch (e) { ... } for most casses
A package I found called await-to-js can also help it.
import to from 'await-to-js';
const [err, users] = await to(getUsers());
if(err) doSomething();
The idea is like Lyes CHIOUKH's method, just a wrapper. Copied the source code here.
/**
* #param { Promise } promise
* #param { Object= } errorExt - Additional Information you can pass to the err object
* #return { Promise }
*/
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
): Promise<[U | null, T | undefined]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
Object.assign(err, errorExt);
}
return [err, undefined];
});
}
export default to;
If you have such above single line async/await function, then this would have been clean code for you:
const getUsers = async (time, shouldReject=false) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldReject) {
reject(Error('Rejected...'));
} else {
resolve(["User1", "User2"]);
}
}, time);
});
}
const userOperation = users => {
console.log("Operating user", users);
}
// Example 1, pass
getUsers(100)
.then(users => userOperation(users))
.catch(e => console.log(e.message));
// Example 2, rejected
getUsers(100, true)
.then(users => userOperation(users))
.catch(e => console.log(e.message));
And for multiple await in a single async function, it would good to have try/catch block as below:
const users = async () => {
try {
const value = await getUsers(1000, false);
const value1 = await getUsers2(1000, false);
...
} catch (error) {
...
}
}

return value of promise

Im new to promises and I'm trying to to return the value of a promise like mongoose does but using mongoskin and bluebird. This works with mongoose return User.find().then(users => users). This will return a list of users not a promise in apollo-server resolvers.
I've tried promise generator and async but with no luck. From what I've read a promise always returns a promise so no idea how mongoose is returning a value.
mongodb.js
import Promise from 'bluebird';
import mongoskin from 'mongoskin';
Object.keys(mongoskin).forEach(function (key) {
var value = mongoskin[key];
if (typeof value === 'function') {
Promise.promisifyAll(value);
Promise.promisifyAll(value.prototype);
}
});
Promise.promisifyAll(mongoskin);
export default {
connect (uri) {
return mongoskin.db(uri, {native_parser:true});
}
};
users.js
import mongodb from '../../databases/mongodb';
export default class User {
constructor () {
this.db = mongodb.connect('mongodb://127.0.0.1:27017/test', {native_parser:true});
this.collection = this.db.collection('users');
}
find (query = {}, options = {}) {
const findAsync = () => {
return Promise.resolve().then(() => {
return this.collection.findAsync(query, options);
})
.then((xx) => {
xx.toArray((err, items) => {
if (err) return err;
return items;
});
});
};
async function getData () {
let foo = await findAsync();
return foo;
}
return getData();
}
}
const user = new User();
function bar () {
return user.find().then(x => console.log(x));
}
console.log(bar());
Your code seems overly complicated. I think what you want is this (I didn't promisify mongoskin because it's not very well suited for that; see below):
export default class User {
constructor() {
this.db = mongoskin.connect(...);
this.collection = this.db.collection('users');
}
find (query = {}, options = {}) {
return new Promise((resolve, reject) => {
this.collection.find(query, options).toArray((err, items) => {
if (err) return reject(err);
resolve(items);
});
});
}
}
I have to say that Mongoskin feels pretty outdated. It's a skin on top of the official mongodb driver, which is pretty decent nowadays (it supports promises out-of-the-box, for one, something that Mongoskin doesn't propagate).
If you do want to promisify, then I should say that the following is a promise anti-pattern:
return Promise.resolve().then(() => {
return this.collection.findAsync(query, options);
}).then(...)
You can rewrite it to this:
return this.collection.findAsync(query, options).then(...);
However, the toArray() makes things difficult again, because for that you do need to create a new promise, so the code becomes something like this:
return this.collection.findAsync(query, options).then((cursor) => {
return new Promise((resolve, reject) => {
cursor.toArray((err, items) => {
if (err) return reject(err);
resolve(items);
});
});
});
Which doesn't look very pretty at all, hence my choice to not promisify and just use callbacks in this case (although I'm sure that Bluebird has some nice tools that may the above code easier to look at, but still...).

Categories

Resources