How to wait for asynchronous function to be executed? - javascript

I am fetching cricket matches and scores from different http requests. The first one fetch match list(with unique ids) and second one fetch scores using unique id. I need the second http request(data.map function) to be completed, then data variable value to be sent(in res.json without using timeout). I know using Promises/Callbacks, but I am confused with code. Currently using setTimeout to wait, but I dont want to use timeout. Please Help.
app.get('/api/matches', (req, res) => {
let url = `http://cricapi.com/api/matches?apikey=${key}`
request(url, { json: true }, (err, resp, body) => {
if (err) return res.json({
error: 1,
msg: err,
})
let data = body.matches.filter(match => {
return match.matchStarted
})
data.map((element, index) => {
let requrl = `http://cricapi.com/api/cricketScore?apikey=${key}&unique_id=${element.unique_id}`
request(requrl, { json: true }, (err, resp, body) => {
element.score = body.score
data.push(element)
})
})
setTimeout(()=>{
res.json({
error: 0,
matches: data
})
},2000)
})
})
Expecting output to be cricket matches with their score, but without timeout function, current output is undefined.

Try wrapping map inside promise like this.
app.get('/api/matches', (req, res) => {
let url = `http://cricapi.com/api/matches?apikey=${key}`
request(url, { json: true }, (err, resp, body) => {
if (err) return res.json({
error: 1,
msg: err,
})
let data = body.matches.filter(match => {
return match.matchStarted
})
let newData = data.map((element, index) => {
return new Promise((resolve, reject) => {
let requrl = `http://cricapi.com/api/cricketScore?apikey=${key}&unique_id=${element.unique_id}`
request(requrl, { json: true }, (err, resp, body) => {
element.score = body.score
resolve(element);
})
});
})
Promise.all(newData).then(data => {
res.json({
error: 0,
matches: data
})
})
})
})

You should use async/await to wait your requests to finish, so what you can do is, so you need to use request-promise package which supports promises, so you can use async await, see at their documentation here
npm install request-promise
Implement async/await as below
const request = require('request-promise');
app.get('/api/matches', async (req, res) => {
let url = `http://cricapi.com/api/matches?apikey=${key}`
let { response, body } = await request({ uri: url, method: 'GET' })
if (response.statusCode !== 200){
return res.json({ error: 1, msg: err,})
}
let data = body.matches.filter(match => {
return match.matchStarted
})
await Promise.all(data.map(async (element, index) => {
let { response, body } = await request({ uri: url, method: 'GET' })
element.score = body.score
data.push(element)
}))
return res.json({ error: 0, matches: data })
}

Wrap every request in a Promise and chain them.
Psuedo code:
// Promise for match
const getMatch = new Promise((resolve, reject) => {
// Do request, call resolve (or reject) when completed.
request(url, resolve);
});
// Promise for score
const getScore(id) = new Promise((resolve, reject) => {
// Do request, call resolve (or reject) when completed.
request(url, resolve);
});
// Chain promises
getMatch()
.then(match => getScore(match))
.then(profit => console.log(profit)
.catch(error => console.warn(error)

Related

Getting PostgresSQL 42703 error (invalid column error)

I am working on a React project with PostgreSQL database, this is the first time I am using it, and I am getting 42703 error on querying a particular column.
Below is the code I have written to query
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id=${userId}`, (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}
I have defined this getList function and then I am making an api call to call this function by passing the userId like this
app.get(`/expenses`, verifySession(), async (req, res) => {
const userId = req.session.userId;
database.getList(userId)
.then(response => {
res.status(200).send(response);
})
.catch(error => {
res.status(500).send(error);
})
})
I even tried passing the userId directly as shown below , still it gives me the same error , which probably means I am querying in a wrong way
app.get(`/expenses`, verifySession(), async (req, res) => {
//const userId = req.session.userId;
database.getList('17a6dea6-a63e-4da7-9910-df7eddb672e6')
.then(response => {
res.status(200).send(response);
})
.catch(error => {
res.status(500).send(error);
})
})
Only when I directly write the string in the query it works properly like this
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id='17a6dea6-a63e-4da7-9910-df7eddb672e6'`, (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}
Can someone please help we with what is exactly going wrong and if my syntax is correct or not ?
This is the frontend part of where I am calling the api.
function getDataForUser() {
fetch(`http://localhost:3001/data`)
.then(response => {
return response.json();
}).then(data => {
console.log(data.rows[0]);
})
}
This problem happened because you didn't use a single quotation for string type in the query. When using where id=${userId} and called with 17a6dea6-a63e-4da7-9910-df7eddb672e6 converted to where id=17a6dea6-a63e-4da7-9910-df7eddb672e6 and this make problem.
You can use two scenarios to handle it:
Use the single quotation for string type:
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id='${userId}'`, (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}
Use parameter binding (As default it converted type)
const getList = (userId) => {
return new Promise(function (resolve, reject) {
pool.query(`SELECT items FROM public."user" where id=$1`, [userId], (error, results) => {
if (error) {
reject(error)
}
resolve(results);
})
})
}

I want to show all my tables from my database to an API

I have 3 tables (services, practitioners, network) in my postgres database and I want them all to show in my API, but I got this error
(node:21300) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
and this is the only output I could get
json response
here is my code.
const handler = (req, res, db, tableName) => {
try{
db.select('*').from(tableName)
.then(data => {
if(data.length){
res.status(200).json({tableName: data});
}
})
}catch(err){
res.status(400).json(err)
}
}
const content = (req, res, db) => {
handler(req, res, db, 'services')
handler(req, res, db, 'practitioners')
}
module.exports = { content };
edit:
here's what I did from Nabil Farhan's answer, and it's working just what I wanted. screenCapture
const getData = async (db, tableName) => {
return new Promise((resolve, reject) => {
db.select('*').from(tableName)
.then(data => {
resolve({[tableName]:data})
})
});
}
const contentHandler = async (req, res, db) => {
// get the argument from get request
let allTable = [];
const table_a = await getData(db, 'services');
const table_b = await getData(db, 'practitioners');
allTable.push(table_a);
allTable.push(table_b);
res.status(200).json({data: allTable});
}
module.exports = { contentHandler };
I recommend you to use promise like this:
async function getData(tableName) {
return new Promise(function (resolve, reject) {
db.select("*")
.from(tableName)
.then((data) => {
resolve(data);
});
});
}
async function run(){
var allTable = [];
const firstTable = await getData('testingDB');
const secondTable = await getData('user');
allTable.push(firstTable);
allTable.push(secondTable);
res.status(200).json({data: allTable});
}
run().then(() => {
console.log("Finished!");
})
I see a couple potential issues here.
Unhandled Promise rejection:
Add a catch block after then (you can rethrow to put it in the existing catch block).
.then(data => {
if(data.length){
let responseData = {}
responseData[tableName] = data
res.status(200).json(responseData)
}
})
.catch(err) {throw err}
tableName is also not going to be interpreted as variable, but as a literal property name, so I changed it to get what I think you were going for.
For the header error, you are setting the response twice, once for "services", then again for "practitioners". One possible solution is to remove the res.status... line from handler and use Promise.all in content and moving the response setting there:
const content = (req, res, db) => {
Promise.all(
handler(req, res, db, 'services')
handler(req, res, db, 'practitioners')
)
.then(allData => {
//use reduce/Object.assign to combine the individual data objects
allData.reduce((finalResponseData, data) => {
if(!data.length) return finalResponseData
Object.assign(finalResponseData, data)
})
.then(finalResponseData => res.status(200).json(finalResponseData)
.catch(err) {res.status(400).json(err)}
}

Trying to refactor a promisified function in to try-catch block

I am trying to refactor this code using try-catch blocks:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
}).catch(error => console.log(error));
}
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
}).catch(error => console.log(error));
}
However I'm not getting the same result - the asynchronousity seems to be being handled differently. Here is my attempt at refactoring:
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxxxx",
'firebase_phone[access_token]': accessToken,
};
await createSession();
try {
ConnectyCube.login(userCredentials, (error, user) => {
return user;
})
}
catch (error) {
console.log(error)
}
}
const createSession = () => {
try {
ConnectyCube.createSession((error, session) => {
return session.user
})
} catch (error) {
console.log(error);
}
}
Is there any particular part of what I'm wrong? Thanks.
Callback-based APIs don't readily turn into something you can use for async/await (which under the hood uses promises). You'll have to "promisify" them first (i.e. wrap them in promises).
Here's an example of what I'm trying to say:
// Promisify these callback-based APIs.
const login = userCredentials => {
return new Promise((resolve, reject) => {
ConnectyCube.login(userCredentials, (error, user) => {
user ? resolve(user) : reject(error);
})
})
})
const createSession = () => {
return new Promise((resolve, reject) => {
ConnectyCube.createSession((error, session) => {
session ? resolve(session.user) : reject(error)
})
})
})
// Then use them in an async function
export const authorizeConnectyCube = async (accessToken) => {
const userCredentials = {
provider: 'firebase_phone',
'firebase_phone[project_id]': "xxxxxxxx",
'firebase_phone[access_token]': accessToken,
}
try {
await createSession()
return login(userCredentials)
} catch (e) {
console.warn(e)
}
}
Also, async functions return promises, with the resolved value being the return value, and the rejected value being any uncaught error thrown inside. A value wrapped in a promise as return value for an async function is redundant.
If you're using Node 8+, it has a utility called promisify which accepts a callback-based API and returns a promise-returning version of it.

JavaScript: Promise.all returning undefined

I'm trying to create a user account creation script with a focus on unique usernames - a prefix and a suffix from a pool, a list of existing usernames, and a list of reserved usernames.
That's just the start of it (no saving yet!), and already that would require three connections, so I just decided to see if I can code a function that would handle them all.
Here's my code so far - and it's on AWS Lambda, and tested via API Gateway, if that means anything:
const dbConnMysql = require('./dbController');
var methods = {
createUser: function() {
let getPrefixSuffixList = new Promise((resolve, reject) => {
let connection = dbConnMysql.createConnection();
dbConnMysql.startConnection(connection)
.then((fulfilled) => {
let table = 'userNamePool';
return dbConnMysql.selectFrom(connection, table, '*', null);
})
.then((fulfilled) => {
console.log(fulfilled);
return dbConnMysql.closeConnection(connection)
.then((fulfilled) => {
resolve(fulfilled);
});
})
.catch((error) => {
console.log(error);
reject(error);
});
});
let getTempUserNameList = new Promise((resolve, reject) => {
// Same as getPrefixSuffixList, different table
});
let getRealUserNameList = new Promise((resolve, reject) => {
// Same as getPrefixSuffixList, different table
});
return new Promise((resolve, reject) => {
Promise.all([getPrefixSuffixList, getTempUserNameList, getRealUserNameList])
.then((fulfilled) => {
console.log(fulfilled[0]);
console.log(fulfilled[1]);
console.log(fulfilled[2]);
let response = {
"statusCode": 200,
"headers": {"my_header": "my_value"},
"body": {"Prefix Suffix":fulfilled[0], "Temp UserName List":fulfilled[1], "Real UserName List":fulfilled[2]},
"isBase64Encoded": false
};
resolve(response);
})
.catch((error) => {
let response = {
"statusCode": 404,
"headers": {"my_header": "my_value"},
"body": JSON.stringify(error),
"isBase64Encoded": false
};
reject(response);
})
});
}
};
module.exports = methods;
This function is called elsewhere, from index.js:
app.get('/createUserName', function (req, res) {
var prom = Register.createUser();
prom.then((message) => {
res.status(201).json(message);
})
.catch((message) => {
res.status(400).json(message);
});
})
Now I'm not entirely sure if what I did with the Promise.All is correct, but from what little I know, if one promise fails, the Promise.All fails.
However, the individual promises do work just fine, and log out the respective results from the database. But inside the Promise.All, it all just logs out undefined.
Is there something I'm missing?
The cause of your problem is this. You need to run the functions, these then return the promise that will eventually resolve:
Promise.all([getPrefixSuffixList(), getTempUserNameList(), getRealUserNameList()])
Here is some simpler code as well. In general there is no need for new Promise(). This code may fix other issues. Also, the undefined could be being printed from any part of the code, make sure it's being printed where you think it is.
// Dummy MySQL connector
const dbConnMysql = {
createConnection: () => 'Connection',
startConnection: conn => new Promise(resolve => setTimeout(resolve, 100)),
selectFrom: (conn, t, q, n) =>
new Promise(resolve =>
setTimeout(() => {
console.log(`${conn}: SELECT ${q} FROM ${t}`);
resolve(`x ${t} RECORDS`);
}, 100)
),
closeConnection: conn => new Promise(resolve => setTimeout(resolve, 100)),
};
const methods = {
createUser() {
const getPrefixSuffixList = () => {
const connection = dbConnMysql.createConnection();
return dbConnMysql
.startConnection(connection)
.then(() => {
const table = 'userNamePool';
return dbConnMysql.selectFrom(connection, table, '*', null);
})
.then(fulfilled => {
console.log(fulfilled);
return dbConnMysql.closeConnection(connection).then(() => fulfilled);
})
.catch(error => {
console.log(error);
// Note: this catch will stop the error from propagating
// higher, it could also be the cause of your problem.
// It's okay to catch, but if you want the error to
// propagate further throw a new error here. Like this:
throw new Error(error);
});
};
const getTempUserNameList = () => {
// Same as getPrefixSuffixList, different table
};
const getRealUserNameList = () => {
// Same as getPrefixSuffixList, different table
};
return Promise.all([getPrefixSuffixList(), getTempUserNameList(), getRealUserNameList()])
.then(fulfilled => {
console.log('fulfilled[0]: ', fulfilled[0]);
console.log('fulfilled[1]: ', fulfilled[1]);
console.log('fulfilled[2]: ', fulfilled[2]);
return {
statusCode: 200,
headers: { my_header: 'my_value' },
body: {
'Prefix Suffix': fulfilled[0],
'Temp UserName List': fulfilled[1],
'Real UserName List': fulfilled[2],
},
isBase64Encoded: false,
};
})
.catch(error => ({
statusCode: 404,
headers: { my_header: 'my_value' },
body: JSON.stringify(error),
isBase64Encoded: false,
}));
},
};
methods.createUser();

Promise returns wrong value

In my code I try to assign a value to json variable to return it after (because I can't return it from the anon. function).
As my function is async, because it sends requests (maybe someone knows how to make it sync? I didn't plan to make it asynchronous), I've added await before the request (https.get).
I've been trying to get value from the Promise, but it's always undefined, even though I've awaited the async function.
Here's a code:
async function get_users() {
const https = require('https');
var token = '...';
var json = undefined;
await https.get('...', (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
json = JSON.parse(data)['response']['items'];
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
return json;
}
get_users().then(function(result) {
console.log(result);
});
Return a Promise and resolve it, when the end event is called, otherwise reject it in case of an error occurred:
async function get_users() {
const https = require('https');
const token = '...';
return new Promise((resolve, reject) => {
https.get('...', resp => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
let json;
try {
json = JSON.parse(data)['response']['items'];
} catch (e) {
reject(e);
};
resolve(json);
});
}).on("error", err => reject(err));
});
}
get_users().then(result => console.log(result));
Please refer my below code.I had issues with getting responses from Promises too.But i finally got it to work.Here's the code:
var output;
var rp = require('request-promise-native');
var myJSONObject = {
"inputs": [{
"name": "<name>",
"value": < value >
}]
};
var orchName = 'TEST05';
postData = JSON.stringify(myJSONObject);
return networkCall(postData, orchName).then((response) => {
console.log('response is' + response)
}).catch((response) => {
console.log(`ERROR: ` + response);
});
function networkCall(postData, orchName) {
return new Promise((resolve, reject) => {
var options = {
method: 'post',
uri: '<URL>',
body: postData,
auth: {
'user': 'usr',
'pass': 'pwd'
},
json: true
};
return rp(options)
.then(body => {
var response = body;
resolve(response);
})
.catch(err => {
console.log('FAILED' + err);
reject(err);
});
});
}
This way your code can run in Synchronous Flow.If the return value is undefined,then,what might have probably happened is that the calling function would have finished executing even before the called function returns its response.But the above approach would work just fine.

Categories