Class/ function result use in following code - javascript

I am a beginner in javascript and am now trying to understand the subject of classes. I've defined the following class. Now I would like to use the result outside of this in a variable.
The class looks like this:
class Maad{
constructor(name, NumWeek, NumMonth){
this.name =name;
this.NumWeek = NumWeek;
this.NumMonth = NumMonth;
}
queryMaad(){
const mongodb =require('mongodb');
const client = require('mongodb').MongoClient;
const url= 'mongodb://localhost:27017/vertrieb';
client.connect(url,(error, db) =>{
if(!error){
console.log("month_log steht")
};
let col = db.collection("umsatz5");
col.aggregate([{'$match': {'AD': this.name}}, {'$match': {'Kalenderwoche':this.NumWeek}}, {'$count': 'procjetnumber'}],function(err, result){
if (err) {
console.error("Error calling", err);
}
console.log(result[0].projectnumber);
result[0].projectnumber;
})
db.close();
});
}
}
My request is:
let ma_1 = new Maad("Hans Wurst", NumWeek);
ma_1.queryMaad();
How can I save the result (the number of projects) in a variable to use it outside of the class? Thanks for your help.

In general, you would assign it basically the way that you assign anything:
const ma_1 = new Maad("Hans Wurst", NumWeek);
const myVar = ma_1.queryMaad();
However your method is a void method which doesn't return anything, so you need to edit your class if you want to get the number of projects.
Returning something from the function is harder than it sounds because MongoClient.connect is a void method which uses callbacks rather than returning a Promise of a response. Honestly I would recommend using a library like mongoose. But it is possible to make the method asynchronous ourselves by returning a new Promise which we resolve or reject based on the callbacks.
class Maad {
constructor(name, NumWeek, NumMonth) {
this.name = name;
this.NumWeek = NumWeek;
this.NumMonth = NumMonth;
}
async queryMaad() {
const url = "mongodb://localhost:27017/vertrieb";
return new Promise((resolve, reject) => {
MongoClient.connect(url, (error, db) => {
if (error) {
reject(error);
}
console.log("month_log steht");
let col = db.collection("umsatz5");
col.aggregate(
[
{ $match: { AD: this.name } },
{ $match: { Kalenderwoche: this.NumWeek } },
{ $count: "procjetnumber" }
],
function (err, result) {
if (err) {
reject(err);
}
resolve(result);
}
);
db.close();
});
});
}
}
Now queryMaad is an async method, one which returns a Promise. That Promise will resolve to the result of col.aggregate on success (you could also resolve to result[0].projectnumber). The promise will reject if there is an error in the connect or col.aggregate methods.
You now get your value like so (probably inside of a function which is itself async):
const result = await ma_1.queryMaad();
You can catch rejection errors here, or allow them to be thrown and catch them higher up.
try {
const result = await ma_1.queryMaad();
} catch (error) {
// console.error or whatever
}

Related

Issue in promise based recursive function

I am working on AWS Lambda using nodejs environment. I have one API in which I am using recursive function for some functionality.
Actually most people say avoid recursive function but as per my functionality I need this. My functionality is as follows.
I have one table table1 in which there are two columns pid and cid which is defined as unique constraint. Which means combination of two columns should be unique.
So if I am inserting any combination in table1 and it that combination already exist then it gives me duplicate entry error which is correct as per my functionality.
So in order to handle this duplicate entry error I have used try..catch block. So in catch block I have checked if duplicate entry error occurred then I am calling one recursive function which try different combination until new entry is created in table1.
And my recursive function is promise based. but when new entry in created successfully then I am resolving the promise. But promise does not get returned from where I have called my recursive function for very first time. And because of that timeout occurs.
So please someone suggest me solution so that my promise got resolved and my functionality will continue from point where I have called my recursive function for very first time. So the timeout will not come.
I am providing my code for reference.
var mysql = require('mysql');
var con = mysql.createConnection({
"host": "somehost.com",
"user": "myusername",
"password": "mypassword",
"database": "mydatabase"
});
exports.handler = async (event, context) => {
try {
con.connect();
} catch (error) {
throw error;
return 0;
}
let tableId = '';
let count = '';
try {
var tempUsageData = {
user_id: userId,
code: code,
platform: source,
some_id: some_id,
count: count
};
dbColumns = 'user_id, code, platform, added_on, count, some_id';
let usageData = [
[userId, code, source, new Date(), count, some_id]
];
var tableInsert = await databaseInsert(con, constants.DB_CONSTANTS.DB_USAGE, dbColumns, usageData);
tableId = tableInsert.insertId;
} catch (error) {
console.log('## error insert table1 ##', error);
if (error.errno == 1062) {
try {
// calling recursive function here
let newTableData = await createTableEntry(con, tempUsageData);
tableId = newTableData.new_usage_id;
count = newTableData.new_count;
} catch (error) {
console.log('Error', error);
return 0;
}
} else {
return 0;
}
};
console.log('## EXECUTION DONE ##');
return 1;
}
var createTableEntry = (con, dataObject) => {
return new Promise(async function (resolve, reject) {
console.log('createTableEntry Called for count', dataObject.count);
try {
var newCounter = await getDataFromDatabase(con, dataObject.some_id);
dbColumns = 'user_id, code, platform, added_on, count, some_id';
let tableData = [
[userId, code, source, new Date(), Number(newCounter[0].counter + 1), some_id]
];
var tableInsert = await databaseInsert(con, 'table1', dbColumns, tableData);
let response = {
new_table_id: tableInsert.insertId,
new_count: Number(newCounter[0].counter + 1)
}
return resolve(response);
//function not returning from here once successful entry done and timeout occures
} catch (error) {
console.log('## ERROR ##', error);
if (error.errno == 1062) {
console.log('## CALL FUNCTION AGAIN ##');
dataObject.count = Number(newCounter[0].counter + 1);
await createTableEntry(con, dataObject);
} else {
return reject(error);
}
}
});
};
My final output should be message "EXECUTION DONE" should be displayed once execution done.
Please suggest me good solution for this. Thanks in advance.
Update your catch block
catch (error) {
console.log('## ERROR ##', error);
if (error.errno == 1062) {
console.log('## CALL FUNCTION AGAIN ##');
dataObject.count = Number(newCounter[0].counter + 1);
let result = await createTableEntry(con, dataObject);
return resolve(result);
} else {
return reject(error);
}
}

How to properly implement mongodb async/await inside a promise?

I've read that having an async inside a Promise is anti-pattern for async/await. The code below works, but I am curious how else to achieve the same result without having async in Promise.
If I remove it, the linter would tell how I can't use await in my mongodb query. If I remove the await in the mongodb query, then it wouldn't wait for the result.
export const getEmployees = (companyId) => {
return new Promise(async (resolve, reject) => {
const employees = await Employees.find(
{ companyId },
);
// other logic here...
resolve({
employees,
});
});
Thanks.
async functions automatically return Promises already, which resolve with whatever expression is eventually returned. Simply make getEmployees an async function:
export const getEmployees = async (companyId) => {
const employees = await Employees.find(
{ companyId },
);
// other logic here...
return { employees };
};
(but make sure to catch in the consumer of getEmployees just in case there's an error)
As #CertainPerformance answered, that is perfect way to retrieve data from mongoDB using async/await, I would like to add some more information about how to handle errors in this case for correctness of the system, and better error handle to return better status to the client about his request.
I'd say it , you usually want to catch all exceptions from async/await call.
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
console.error(error);
}
Now let's check the ways we can handle our errors that might occur.
Handle error inside error scope.
Assign a default value to the variable in the catch block.
Inspect error instance and act accordingly.
This is the most common way to handle errors in those cases and most elegant way in my opinion.
Handle error inside error scope:
export const getEmployees = async (companyId) => {
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
console.error(error);
}
};
Assign a default value to the variable in the catch block:
export const getEmployees = async (companyId) => {
let employees;
try {
employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
employees = employees;
} catch (error) {
console.error(error);
}
if (employees) { // We received the data successfully.
console.log(employees)
// Business logic goes here.
}
return employees;
};
Inspect error instance and act accordingly:
export const getEmployees = async (companyId) => {
try {
const employees = await Employees.find({
companyId
});
// You can add more logic here before return the data.
return {
employees
};
} catch (error) {
if (error instanceof ConnectionError) {
console.error(error);
} else {
throw error;
}
}
};
Some more explanations about async await and more useful methods that you can find in those answers.
How run async / await in parallel in Javascript

Using async/await and try/catch to make tiered api calls

SO friends!
I need to make a call to an api; if it fails, I need to make a call to same api with different param; if it fails AGAIN, I need to make a call to same api with a third, different param; if it fails finally after that, is an actual error and can bugger out.
Only way I can figure to do this is with nested try/catch statements, ala:
const identityCheck = async (slug) => {
let res;
try {
res = await Bundle.sdk.find(slug);
} catch (err) {
console.log('Fragment didn\'t work ========', slug, err);
try {
res = await Bundle.sdk.find(`package/${slug}`);
} catch (e) {
console.log('Fragment didn\'t work package ========', e);
try {
res = await Bundle.sdk.find(`${slug}-list`);
} catch (error) {
console.log('None of the fragments worked================.', error);
}
}
}
return logResponse(res);
};
identityCheck('fashion');
But seems like there MUST be another simpler way to do this. I tried boiling down into a retry function, but that just ends up being even more code and way less clear:
const identityCheck = (slug) => {
const toTry = [
slug,
`package/${slug}`,
`${slug}-list`
];
return new Promise((resolve, reject) => {
let res;
let tryValIndex = 0;
const attempt = async () => {
try {
res = await Bundle.sdk.find(toTry[tryValIndex]);
return resolve(logResponse(res));
} catch (err) {
console.log(`toTry ${toTry[tryValIndex]} did not work ========`, slug, err);
if (tryValIndex >= toTry.length) {
return reject(new Error('Everything is broken forever.'));
}
tryValIndex++;
attempt();
}
};
attempt();
});
};
Guidance and opinions appreciated!
Avoid the Promise constructor antipattern, and use a parameter instead of the outer-scope variable for the recursion count:
function identityCheck(slug) {
const toTry = [
slug,
`package/${slug}`,
`${slug}-list`
];
async function attempt(tryIndex) {
try {
return await Bundle.sdk.find(toTry[tryIndex]);
} catch (err) {
console.log(`toTry ${toTry[tryIndex]} did not work ========`, slug, err);
if (tryIndex >= toTry.length) {
throw new Error('Everything is broken forever.'));
} else {
return attempt(tryIndex+1);
}
}
}
return attempt(0);
}
Following on Bergi's answer, but trying to preserve the original structure to avoid "even more code":
const idCheck = async (slug, alsoTry = [`package/${slug}`, `${slug}-list`]) => {
let res;
try {
res = await Bundle.sdk.find(slug);
} catch (err) {
if (!alsoTry.length) throw err;
return idCheck(alsoTry.shift(), alsoTry);
}
return logResponse(res);
};
idCheck('fashion');
This takes advantage of default arguments being quite powerful.
Same complexity, but aesthetically closer to the nested try-blocks, a simpler pattern perhaps.

Angular2 change service method from callback to Async

I started a simple Angular2 Electron app, and I have a service method querying a local SQL Server database. Everything works fine so far. Now I am trying to get the results of the service DB call to my component and display it somehow.
The problem is that the query logic is written more for callback syntax:
sql.query(sqlString, (err, result) => {
...
callback(result);
...
});
I'm having a hard time rewriting it to return a promise, since the result will always be within result parameter of the query command function. My component looks like this:
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
loadMyData(id: number): void {
let tcData = this.myService.getMyData();
tcData.forEach(element => {
this.results += element.FileName + " " + "....\n";
});
};
}
And my service looks like this:
import { Injectable } from "#angular/core";
import * as sql from "mssql";
#Injectable()
export class MyService {
getMyData():Array<MyItem> {
let myData:Array<MyItem> = [];
let config = {
user: "sa",
password: "xxx",
server: "localhost",
database: "mydb"
};
const pool1 = new sql.ConnectionPool(config, err => {
if (err) {
console.log("connect erro: " + err);
}
let q:string = `SELECT TOP 10 * FROM MyTable;`;
let final = pool1.request()
.query<MyItem>(q, (err, result) => {
if (err) {
console.log("request err: " + err);
}
console.log("db result count: " + result.recordsets[0].length);
result.recordsets[0].forEach(row => {
myData.push(row);
});
});
});
return myData;
}
}
I do get a result back, but the component never sees it since it comes back before the results are returned.
I've tried doing an await on the query call, within the ConnectionPool function, but I get an error stating that await can only be called within an async function, even though I have async set on that method. The mssql package has an Async/ Await section, but the given syntax on that page gives errors, when I try it.
Any idea how I can write this using a promise?
As you pointed out, there are 3 way to handle async functions: using callback, using promise, and using Async/ Await. I will try to show all three ways but you should learn about event loop in javascript and how it takes care of async functions.
Callback
Callback is technically fastest way to handle async functions but it is quite confusing at first and might create something called callback hell if not used properly. Callback hell is very terrible that someone even created a website for it http://callbackhell.com/.
So you code can be rewritten as:
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
loadMyData(id: number): void {
// call getMyData with a function as argument. Typically, the function takes error as the first argument
this.myService.getMyData(function (error, tcData) {
if (error) {
// Do something
}
tcData.forEach(element => {
this.results += element.FileName + " " + "....\n";
});
});
};
}
Service
import { Injectable } from "#angular/core";
import * as sql from "mssql";
#Injectable()
export class MyService {
// Now getMyData takes a callback as an argument and returns nothing
getMyData(cb) {
let myData = [];
let config = {
user: "sa",
password: "xxx",
server: "localhost",
database: "mydb"
};
const pool1 = new sql.ConnectionPool(function(config, err) {
if (err) {
// Error occured, evoke callback
return cb(error);
}
let q:string = `SELECT TOP 10 * FROM MyTable;`;
let final = pool1.request()
.query<MyItem>(q, (err, result) => {
if (err) {
console.log("request err: " + err);
// Error occured, evoke callback
return cb(error);
}
console.log("db result count: " + result.recordsets[0].length);
result.recordsets[0].forEach(row => {
myData.push(row);
});
// Call the callback, no error occured no undefined comes first, then myData
cb(undefined, myData);
});
});
}
}
Promise
Promise is a special object that allows you to control async function and avoid callback hell because you won't have to use nested callback but only use one level then and catch function. Read more about Promise here
Component
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
loadMyData(id: number): void {
this.myService.getMyData()
.then((tcData) => {
// Promise uses then function to control flow
tcData.forEach((element) => {
this.results += element.FileName + " " + "....\n";
});
})
.catch((error) => {
// Handle error here
});
};
}
Service
#Injectable()
export class MyService {
// Now getMyData doesn't take any argument at all and return a Promise
getMyData() {
let myData = [];
let config = {
user: "sa",
password: "xxx",
server: "localhost",
database: "mydb"
};
// This is what getMyData returns
return new Promise(function (resolve, reject) {
const pool1 = new sql.ConnectionPool((config, err) => {
if (err) {
// If error occurs, reject Promise
reject(err)
}
let q = `SELECT TOP 10 * FROM MyTable;`;
let final = pool1.request()
.query(q, (err, result) => {
if (err) {
// If error occurs, reject Promise
reject(err)
}
console.log("db result count: " + result.recordsets[0].length);
result.recordsets[0].forEach((row) => {
myData.push(row);
});
//
resolve(myData);
});
});
})
}
}
Async/await
Async/await was introduced to address the confusion you was having when dealing with callbacks and promises. Read more about async/await here
Component
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
// Look. loadMyData now has to have async keyword before to use await. Beware, now loadMyData will return a Promise.
async loadMyData(id) {
// By using await, syntax will look very familiar now
let tcData = await this.myService.getMyData(tcData);
tcData.forEach((element) => {
this.results += element.FileName + " " + "....\n";
});
};
}
Service would be exactly the same as in Promise because Async/await was created especially to deal with them.
NOTE: I remove some Typescript feature from your code because I am more accustomed to vanilla JS but you should be able to compile them because Typescript is a superset of JS.

Adding bluebird promise to NodeJS module, then function is not defined

I'm new to promise and bluebird, in my current project, I have to deal with a lot async API calls, therefore I want to use JS promise as my main tool.
One of my imported module looks like this:
var Promise = require("bluebird");
var watson = Promise.promisifyAll(require('watson-developer-cloud'));
var conversation = watson.init();
exports.enterMessage = function (inputText) {
var result; //want to return
conversation.message({
input: {
"text": inputText
}
}, function(err, response) {
if (err) {
console.log('error:', err);
result = err;
}
else {
result = response.output.text;
}
});
console.log(result); //sync call, result === undefined
return result;
}
My question is how should I approach this question? I understand the example about using promise with IO such like fs. So I try to mimic the way by doingconversation.message(...).then(function(response){result = response.output.text}), but it says conversation.message(...).then() is not defined.
Thanks to jfriend00's link, I fixed my logic and used the correct way to handle this async call.
Here is the fixed code:
//app.js
var Promise = require("bluebird");
var conversation = Promise.promisifyAll(require('./watson-conversation'));
conversation.enterMessage(currentInput).then(function(val){
res.send(val)}
).catch(function(err){
console.log(err)
});
});
//watson-conversation.js
var conversation = watson.init();
exports.enterMessage = function (inputText) {
return new Promise(function(resolve, reject){
conversation.message({
input: {
"text": inputText
}
}, function(err, response) {
if (err) {
console.log('error:', err);
reject(err);
}
else {
resolve(response.output.text);
}
});
});
}

Categories

Resources