why promise.resolve lost value inside of async function? - ecmascript-6

I know when we use promise in JavaScript, we usally use two ways like below.
var f1 = () => {
return new Promise( (resolve, reject) => {
resolve(10);
})
}
var f2 = () => {
return Promise.resolve(10)
}
f1().then( data => { console.log(data) }) // 10
f2().then( data => { console.log(data) }) // 10
But if i use async function inside of promise, Promise.resolve lost the value like below.
const fs = require('fs')
var f1 = () => {
return new Promise( (resolve, reject) => {
fs.readFile('data.txt', (err, data) => {
resolve(data);
})
})
}
var f2 = () => {
return Promise.resolve()
.then(() => {
fs.readFile('data.txt', (err, data) => {
//return data --> undefined
//Promise.resolve(data) --> undefined
return Promise.resolve() --> undefined
})
})
}
f1().then( data => { console.log('1,',data) })
f2().then( data => { console.log('2,',data) })
I think that i use wrong way about Promise.resolve,,, OR Promise.resolve not support async function... someone tell me why Promose.resolve fail..

If you have a value that you immediately want to resolve a promise with, you can use Promise.resolve().
If you get the value asynchronously in a callback, you must use the new Promise constructor with the resolve/reject callbacks, there is no way around it.

Related

Return mysql query result using Promise

I'm currently a little confused as to how to properly wait for the promise to finish before returning the result from a query
Here is my current code:
const getLeaderboardValues = async () => {
const SQLConnection = await getSQLConnection();
return new Promise((resolve, reject) => {
SQLConnection.query(getValuesSQLQuery, (err, result) => {
if (err) { reject(err) }
return resolve(result);
});
SQLConnection.end()
})
}
const runtime = () => {
getLeaderboardValues().then((result) => {
console.log(result);
})
}
The code above does log the correct results while debbugging, i believe this is because i'm giving the code more time to render with the breakpoints, however when running normally i get undefined
I believe the SQLConnection.end() line is executing before the query is returned, given it is outside the query statement.
The below may solve your issue, however I do not recommend opening a connection and closing it on every request in production systems.
const getLeaderboardValues = async () => {
const SQLConnection = await getSQLConnection();
return new Promise((resolve, reject) => {
SQLConnection.query(getValuesSQLQuery, (err, result) => {
if (err) {
reject(err)
return SQLConnection.end()
}
SQLConnection.end()
return resolve(result);
});
})
}
const runtime = () => {
getLeaderboardValues().then((result) => {
console.log(result);
})
}

How to print json api data in reactjs

I'm fetching json api details through GET request and trying to print it. Getting an error:
Error in the console is Uncaught ReferenceError: allUsers is not defined
const Dashboard = ({status, juser}) => {
const [allUsers, setAllUsers] = React.useState([]);
const id = juser.actable_id;
console.log(id); //getting id here as 1
const getAllusers = () => {
axios
.get(`http://localhost:3001/user/${id}`, { withCredentials: true })
.then((response) => {
console.log(response.data);
setAllUsers(response.data);
})
.catch((error) => {
console.log(" error", error);
});
};
React.useEffect(() => {
getAllusers();
}, []);
{allUsers.map((job_seeker, index) => {
return (
<div>
<p>{job_seeker.name}</p>
</div>
);
})}
}
export default Dashboard;
I'm new to react. Any help is appreciatable.
const [state, setState] = React.useState([]);
the state is where your data is located and setState is function to reset the state from anywhere,
so on your code,
const [jobseekers, allUsers] = React.useState([]); // change string to array
jobseekers is the variable where your data is located and allUsers is the function to store data into state.
set data to state using allUsers function,
const getAllusers = () => {
axios
.get(`http://localhost:3001/user/${id}`, { withCredentials: true })
.then((response) => {
allUsers(response.data);
})
.catch((error) => {
console.log(" error", error);
});
};
and map from jobseekers
{jobseekers.map((job_seeker, index) => {
return (
<div>
<p>{job_seeker.name}</p>
</div>
);
})}
Also I would suggest to rename your state and setState as,
const [allUsers, setAllUsers] = React.useState([]);
You didn't pass the value of response to allUsers, instead, you just created a new variable. So change
const allUsers = response.data;
to:
allUsers(response.data)
Besides, you can also improve the way that you have used useState. You have initialized it as an empty string while you'll probably store an array from response in jobseekers. So, initialize it as an empty array.
const [jobseekers, allUsers] = React.useState([]);

How could I pass JSON object array result to my api URL? (In REACT)

I have to fetch 2 api from backend, and try to get the result from this two. but, at the moment, the JSON result I get from the first API is object Array in JSON. I need to pass the id from first API(using setState) to second API for path variables. But when I do in my way, it fail to retrieve the data. Consider the code below:
componentDidMount(){
// console.log(loginEmail)
fetch(`http://localhost:9000/api/item/list`,)
.then((resp)=>{
resp.json().then((res)=>{
console.log(res.data);
// localStorage.setItem('id', res.data.user_info.id);
this.setState({data: res.data});
}
)
})
const id = this.state.data.id;
fetch(`http://localhost:9000/api/item/photo/view/${id}`,)
.then((resp)=>{
resp.json().then((res)=>{
console.log(res);
// localStorage.setItem('id', res.data.user_info.id);
this.setState({res});}
)
})
}
The problem is that fetch returns a Promise so, at the line
const id = this.state.data.id;
You do not have data populated yet.
You have to concatenate the two requests in a way like the following:
componentDidMount() {
fetch(`http://localhost:9000/api/item/list`)
.then((resp) => {
// return the id
})
.then((id) => {
fetch(`http://localhost:9000/api/item/photo/view/${id}`)
.then((resp) => {
// do what you need with the result
})
})
}
Fetch is asynchronous, which means javascript will
fetch data on the first call with no waiting, and continue
to the second fetch call where the id is not defined or Null.
In order to fix that you can use promises as follow
My code example
import React from "react";
class Home extends React.Component {
constructor() {
super();
this.state = {
res: [],
}
}
// http://jsonplaceholder.typicode.com/users
fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((resp) => {
resp.json().then((res) => {
console.log(res);
// localStorage.setItem('id', res.data.user_info.id);
resolve(res);
}
)
})
})
}
async componentDidMount() {
let data = await this.fetchData("http://jsonplaceholder.typicode.com/users");
console.log("data :", data);
let id = data[0].id;
console.log("Id :", id);
let newData = await this.fetchData(`http://jsonplaceholder.typicode.com/users/${id}`);
this.setState({ res: newData });
}
render() {
return (
<div>
Call API
</div>
)
}
}
export default Home
Adapted on your code
fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((resp) => {
resp.json().then((res) => {
console.log(res.data);
// localStorage.setItem('id', res.data.user_info.id);
resolve(res.data);
}
)
})
})
}
async componentDidMount() {
// console.log(loginEmail)
let data = await this.fetchData("http://localhost:9000/api/item/list");
let id = data.id;
let newData = await this.fetchData(`http://localhost:9000/api/item/photo/view/${id}`);
this.setState({ res: newData });
}
You need to make sure that each id gets its relevant results.
async componentDidMount() {
await fetch(`http://localhost:9000/api/item/list`)
.then(async (resp) => {
let req_ = resp.map((item)=>{
return await fetch(`http://localhost:9000/api/item/photo/view/${item.id}`)
})
let result = Promise.all(req_)
console.log(result)
})
}

How to get return values from Async/await function when fetching the data from mySQL in Nodejs

I am fetching some exchange data from DB, then extracting the name of distinct exchanges and passing again into MYSQL query to fetch data from a different table.
The problem I am facing is that async await does not return the value rather just return Promise { }.
Below is the code that I am trying, wondering where I am going wrong.
//Function that fetches the exchanges from DB
const getExchange = () => {
return new Promise((resolve, reject) => {
db.connection.query(`
SELECT *
FROM,
(
SELECT
exchange,
COUNT(pair) as noOfMarkets
FROM ticker_data
) as t
`, (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
})
})
}
// push unique exchanges to an array.
const getExchangesData = async () => {
const allExchanges = await getExchanges();
let exchanges = []
allExchanges.forEach(item => {
let exchange = {
exchange: item.exchange
}
exchanges.push(exchange)
})
return await exchanges
}
// mapping through an array of exchanges and passing to DB query to get data from the DB.
const getSingleExchange = async () => {
const exchanges = await getExchangesData()
await Promise.all(exchanges.map(async (item) => {
db.connection.query(`
SELECT
exchange_rank,
name
volume24hUSD
(
SELECT
volume24hUSD as tradingVolYesterday
FROM exchanges
WHERE name = '${item.exchange}'
AND createdAt >= now() -interval 1 day
AND createdAt < now() -interval 1 day + interval 120 second
LIMIT 1
) volumeDay1
FROM exchanges
WHERE name = '${item.exchange}'
`, (err, resp) => {
if (!err) {
console.log(resp) // getting all the values
let volData = {
name: resp[0].name,
exchange_rank: resp[0].exchange_rank,
icon: resp[0].icon
}
return volData
}
})
}))
}
const data = getSingleExchange()
console.log(data) // returning Promise { <pending> }
Edit
After making changes suggested in an answer, I still have an issue:
//Function that fetches the exchanges from DB
const getExchange = () => {
return new Promise((resolve, reject) => {
db.connection.query(`
SELECT *
FROM,
(
SELECT
exchange,
COUNT(pair) as noOfMarkets
FROM ticker_data
) as t
`, (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
})
})
}
// push unique exchanges to an array.
const getExchangesData = async () => {
const allExchanges = await getExchanges();
let exchanges = []
allExchanges.forEach(item => {
let exchange = {
exchange: item.exchange
}
exchanges.push(exchange)
})
return await exchanges
}
// mapping through an array of exchanges and passing to DB query to get data from the DB.
const getSingleExchange = async () => {
const exchanges = await getExchangesData()
await Promise.all(exchanges.map((item) => {
return new Promise((resolve, reject) => {
db.connection.query(`...`, (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
}).then(resp => {
console.log(resp)
let volData = {
name: resp[0].name,
exchange_rank: resp[0].exchange_rank,
icon: resp[0].icon
}
return volData
})
})
}))
}
getSingleExchange().then(data => {
console.log(data)
});
I now get this error:
(node:30583) UnhandledPromiseRejectionWarning: TypeError: db.connection.query(...).then is not a function
at Promise (/getExchanges.js:217:16)
at new Promise ()
at Promise.all.exchanges.map (/getExchanges.js:145:16)
at Array.map ()
at getSingleExchange (/getExchanges.js:144:33)
The main issue is in this part:
await Promise.all(exchanges.map(async (item) => {
That map callback is not returning anything, and it has no await, so using async makes no sense.
Instead remove async:
await Promise.all(exchanges.map((item) => {
... and return a promise in the callback function, much like you had done in the first function:
return new Promise((resolve, reject) => {
db.connection.query(`...`), (err, resp) => {
if (!err) {
resolve(resp)
} else {
reject(err)
}
})
}).then(resp => {
console.log(resp)
let volData = {
name: resp[0].name,
exchange_rank: resp[0].exchange_rank,
icon: resp[0].icon
}
return volData
});
You would benefit from writing one generic function that promisifies query, so that you don't have to do that new Promise-thing for every single query you need.
Finally, you cannot expect to get an asynchronous result synchronously: async functions do not return the asynchronous result synchronously, but return a promise for it. So your last lines (main code) should still await. So either do this:
(async () => {
const data = await getSingleExchange()
console.log(data)
})(); // immediately executing (async) function expression
Or:
getSingleExchange().then(data => {
console.log(data)
});
NB: doing return await exchanges in the second function makes no sense (exchanges is not a promise), so you can just do return exchanges.

ES6 JS Promises - how to avoid conditional nesting

I am trying to write a piece of code using promises, avoiding nesting them but I am stuck in testing the returned results to handle the promises flow ..
Is this pattern workable ??
// set of promise tasks returning values
function doTask1() => {
return apiPromise1()
.then((result1) => {
return result1;
})
}
function doTask2(result1, paramOne) => {
return apiPromise2(result1, paramOne)
.then((result2) => {
return result2;
})
}
function doTask3(result1) => {
return apiPromise3()
.then((result3) => {
return result3;
})
}
function doTask4(result1, paramOne) => {
return apiPromise4()
.then((result4) => {
return result4;
})
}
// main promise to handle the flow of promises according to promises returned results
function getCurrentProcess(paramOne) {
const promises = [];
// how to get the returned result1 to be used by other promises ?
promises.push(doTask1);
if (result1 === 'OK') {
promises.push(doTask2(result1, paramOne));
if (result2 === 'OK') {
promises.push(doTask3(result1));
if (result3 === 'OK') {
promises.push(doTask4(result1, paramOne));
}
}
}
return Promisz.all(promises)
.then(() => {
return 'well done'
});
}
// initial calling function
exports.newJob = functions.https.onRequest((req, res) => {
const paramOne = { ... }
getCurrentProcess(paramOne).then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});
If you want to write promises in more procedural way you need use async/await (ES6). If you need backward compatibility with ES5 you need to use babel or typescript which translate await/async to ES5.
async function getCurrentProcess(paramOne) {
const result1 = await doTask1();
if (result1 === 'OK') {
const result2 = await doTask2(result1, paramOne);
if (result2 === 'OK') {
const result3 = await doTask3(result1);
if (result3 === 'OK') {
await doTask4(result1, paramOne);
}
}
}
return 'well done'
}
Without async/await you need to use promise chain:
doTask1().then((result1)=>{
if (result1 === 'OK') {
...
}
...
})
However it will not produce readable code.
You could write a wrapper function which takes an array of doTaskN as deferred functions:
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(() => conditional(...fns.slice(1)));
};
The idea would be to pass in the reference to the doTask functions so that the conditional function executes them. This can be used like:
conditional(doTask1, doTask2, doTask3, doTask4)
.then(() => {
console.log("all done");
})
.catch(() => {
console.log("failed");
});
Here's a full example of how to use it:
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(result => {
console.log("task:", result);
if(result === "OK") {
return conditional(...fns.slice(1))
}
});
};
const task1 = (param1, param2) => Promise.resolve("OK");
const task2 = (param1) => Promise.resolve("OK");
const task3 = () => Promise.resolve("failed");
const task4 = () => Promise.resolve("OK");
conditional(() => task1("one", 2), () => task2(1), task3, task4)
.then(() => {
console.log("all done");
})
.catch(() => {
console.log("failed");
});
If you want that your promise return result is used by other promises, you shouldn't use Promise.all() method because it doesn't run methods in the order you want, it just waits for all of the promise methods to complete and returns all results.
Maybe something like promise-array-runner would help?
Maybe you could check if result === 'OK' inside your task method? Or create a Factory which takes care of that.
.then((result1) => {
return result1;
})
is a no-op and should be omitted, but I suppose that real code doesn't have this problem.
This is a use case for async function because they can seamlessly handle this sort of control flow, as another answer suggests. But since async is syntactic sugar for raw promises, it can be written in ES6. Since tasks depend on results of each other, they cannot be processed with Promise.all.
This is same case as this one that uses async.
You can bail out from promise chain by throwing an exception and avoid nested conditions with:
// should be additionally handled if the code is transpiled to ES5
class NoResultError extends Error {}
function getCurrentProcess(paramOne) {
doTask1()
.then(result1 => {
if (result1 !== 'OK') throw new NoResultError(1);
return result1;
})
.then(result1 => ({ result1, result2: doTask2(result1, paramOne) }))
.then(({ result1, result2 }) => {
if (result2 !== 'OK') throw new NoResultError(2);
return result1;
})
// etc
.then(() => {
return 'well done';
})
.catch(err => {
if (err instanceof NoResultError) return 'no result';
throw err;
})
}
Since result1 is used in multiple then callbacks, it could be saved to a variable instead of being passed through promise chain.
Promise chain could become simpler if NoResultErrors were thrown in task functions.
Thanks to all feedbacks !!
All answers are rights... however I voted for CodingIntrigue wtapprer function solution in my case...
1 - As i am using Firebase functions , it's still ES5 , I cannot use sync/await. Using babel or typescript only for Firebase functions will result in much more setup work...
2 - I tested various use cases and this pattern is quite easy to understand with JS level... I am sur that it can be improved later..
so finally I got this running ...
let auth = null;
let myList = null;
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(result => {
if(result) {
return conditional(...fns.slice(1));
}
return result;
});
};
const task1 = (param1) => Promise.resolve()
.then(() => {
console.log('TASK1 executed with params: ', param1)
auth = "authObject"
return true;
});
const task2 = (param1, param2) => Promise.resolve()
.then(() => {
console.log('TASK2 executed with params: ', param1, param2)
return true;
});
const task3 = (param1, param2) => Promise.resolve()
.then(() => {
console.log('TASK3 executed with params: ', param1, param2)
myList = "myListObject"
console.log('search for param2 in myList...')
console.log('param2 is NOT in myList task4 will not be executed')
return false;
});
const task4 = (param1) => Promise.resolve()
.then(() => {
console.log('TASK4 executed with params: ', param1)
return true;
});
// FIREBASE HTTP FUNCTIONS ==================
exports.newContactMessage = functions.https.onRequest((req, res) => {
conditional(() => task1("senderObject"), () => task2(auth, "senderObject"), () => task3(auth, "senderObject"), () => task4("senderObject"))
.then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});