Firebase Promise Returning Undefined Data Javascript - javascript

I've followed several guides on how to correctly wait for the data from my ListFile() function. I have logged the output to the console in ListFile() so I know the data is correct. When I try and await for the function to complete and log the data, I get one of two outputs when experimenting with awaits and promises:
getData error: TypeError: undefined is not an object (evaluating 'response.json')
or
Promise {
"_U": 0,
"_V": 0,
"_W": null,
"_X": null,
}
I'm creating a React Native app and the function that calls ListFile() contains a bunch of other components that are rendered so I can't make the entire function an async function. Not sure what else to do here.
Calls ListFile()
const getData = async () => {
try {
const response = await ListFile();
const data = response.json();
console.log(data);
} catch (err) {
console.log("getData error: " + err);
}
}
getData(); //always returns getData error: TypeError: undefined is not an object (evaluating 'response.json')
ListFile()
async function ListFile() {
// Reference Firebase Container
var filesInStorageList = [];
let content = [];
const listRef = ref(fbStorage, filePath);
console.log("Listing Files");
// List all files in container
listAll(listRef)
.then((res) => {
res.prefixes.forEach((folderRef) => {
// All the prefixes under listRef.
// You may call listAll() recursively on them.
console.log(res);
});
res.items.forEach((itemRef) => {
// All the items under listRef.
let fileInStorage = itemRef["_location"]["path_"].slice(filePath.length);
filesInStorageList.push(fileInStorage);
// CORRECT DATA IS LOGGED console.log("Pushing " + fileInStorage);
// CORRECT DATA IS LOGGED console.log(filesInStorageList);
});
return filesInStorageList;
}).catch((error) => {
// Uh-oh, an error occurred!
console.log("ListFile - ERROR");
});
}

You're mixing async/await and Promises. Although they serve similar purposes, generally they are used separately to avoid situations like this where they are used together and then create unexpected results if one doesn't know what to look for.
You annotate your ListFile function with async, which means that it's expecting to find await somewhere. Instead, you use a Promise-based .then and .catch.
To convert your code, you could change it to this:
async function ListFile() {
// Reference Firebase Container
var filesInStorageList = [];
let content = [];
const listRef = ref(fbStorage, filePath);
console.log("Listing Files");
// List all files in container
try {
const res = await listAll(listRef);
//...do your forEach loops
return filesInStorageList;
} catch(err) {
//handle your error
}
}

You should add a return on the listAll method to return its value when calling it from getData(). See sample code below:
const getData = async () => {
try {
ListFile()
.then((response) => {
// do something here.
console.log(response);
});
} catch (err) {
console.log("getData error: " + err);
}
}
getData();
async function ListFile() {
// Reference Firebase Container
var filesInStorageList = [];
let content = [];
const filePath = "test/";
const listRef = ref(storage, filePath);
// List all files in container
return listAll(listRef)
.then((res) => {
res.prefixes.forEach((folderRef) => {
// All the prefixes under listRef.
// You may call listAll() recursively on them.
console.log(res);
});
res.items.forEach((itemRef) => {
// All the items under listRef.
let fileInStorage = itemRef["_location"]["path_"].slice(filePath.length);
filesInStorageList.push(fileInStorage);
// CORRECT DATA IS LOGGED console.log("Pushing " + fileInStorage);
// CORRECT DATA IS LOGGED console.log(filesInStorageList);
// console.log(filesInStorageList);
});
return filesInStorageList;
}).catch((error) => {
// Uh-oh, an error occurred!
console.log(error);
});
}
Also, as #jnpdx stated, you have to use a Promise-based .then and .catch on your ListFile()

Related

TypeError: Cannot read properties of undefined (reading 'id') during async calls inside a Promise

In this piece of code, there is data coming from different sources, and it arrives with different delays. That's why I'm using asynchronous calls, but apparently I'm doing something wrong:
const handleCalculations = async ({
configuration,
product,
}) => {
const configurations = [];
const results = {};
/*
configurations get populated with elements
*/
await Promise.all(configurations.map(async (configuration) => {
const calcResult = await getResults(configuration);
if (calcResult.message !== 'OK') {
console.error('Error occurred');
}
const builtResponse = await getResponse(configuration)
.filter(populatedOnly);
console.log({ result }); // error gets thrown among logged result
const resKey = processed.config.module;
results[resKey] = {}; // initialize property
results[resKey].respId = await calcResult.result.configuration.id; // error relates to this line
results[resKey].result = builtResponse;
}));
return {
message: 'OK',
result: results,
};
};
The error that gets displayed is something like:
TypeError: Cannot read properties of undefined (reading 'id')
How to make sure the data is in place when it's time to read it?
You have to return an array of Promises to Promise.all() function. But in your code you are not returning anything. I am just giving an example similar to your code below.
const configurations = ['https://jsonplaceholder.typicode.com/todos/1', 'https://jsonplaceholder.typicode.com/todos/2'];
const results = {};
let ret = (async function run() {
let calcResult = await Promise.all(
configurations.map(
(configuration) => fetch(configuration)
)
);
calcResult = await Promise.all(
calcResult.map(
(i) => i.json()
)
);
calcResult.map(res => {
results[res.id] = res.title;
});
console.log(results);
})();
Here, fetch() and json() return Promise to Promise.all() function. Promise.all() executed after all the Promises properly resolved. You can check more about it here.

Nodejs wait till async function completes and print the results

I want to wait on the HTTP POST request to complete and then return response to the caller function. I am getting Undefined when I print the received results.
I have defined post method as below:
// httpFile.js
const axios = require('axios');
module.exports = {
getPostResult: async function(params) {
console.log("getPostResult async called...");
var result = await axios.post("https://post.some.url", params)
.then ((response) => {
console.log("getPostResult async success");
return {response.data.param};
})
.catch ((error) => {
console.log("getPostResult async failed");
return {error.response.data.param};
});
}
}
And I am calling it in this way:
// someFile.js
const httpFile = require('./httpFile');
// Called on some ext. event
async function getPostResult() {
var params = {var: 1};
var result = await httpFile.getPostResult(params);
// Getting Undefined
console.log("Done Result: " + JSON.stringify(result));
}
I don't want to handle the .then and .catch in the calling function as I want to return the different values based on the POST result.
How should I wait for the response and get the return results.
In the above code I am getting the log statements as expected and "Done Result" get printed at the very end after the 'getPostResult' returns.
you are using both await & .then thats why it returns undefined.
this is how it should look
// httpFile.js
const axios = require('axios')
module.exports = {
getPostResult: async function (params) {
try {
const res = await axios.post('https://post.some.url', params)
return res.data
} catch (error) {
// I wouldn't recommend catching error,
// since there is no way to distinguish between response & error
return error.response.data
}
},
}
if you want to catch error outside of this function then this is way to go.
getPostResult: async function (params) {
const res = await axios.post('https://post.some.url', params)
return res.data
},

Wait for response from request before returning

I am trying to create a function with a GET request that returns a portion of the data from the GET request. However, it keeps returning before the data is retrieved, so I keep getting "undefined". How can I set this up so it actually waits for the data to be set before returning?
let getInfo = async () => {
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => { // Promise being here DOES work
request.on('response', (response) => {
response.on('data', (chunk) => {
//return new Promise((resolve, reject) => { //Promise being here does NOT work
let body = JSON.parse(chunk)
let info = body.data
if (info){
resolve(info);
}
reject();
//})
});
});
request.write('')
request.end()
}).then(data => {
console.log("From then: "+data)
return data
})
}
getInfo().then(data => {
console.log("From outside: "+data)
})
Edit: This is the updated version that still does not work. I am trying to use the native electron method and I don't see why this doesn't work. The "From then:" part displays the info correctly. But when run "From outside:" it prints undefined. Does the issue have anything to do with the response.on being nested inside the request.on?
Solution: As #NidhinDavid showed in his answer, the issue was that the promise was inside the 'response' listener. Moving the 'GET' request from start to finish inside the Promise fixed it to giving the correct output. I have updated my code to reflect that for future individuals.
let getInfo = () => {
let info;
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => {
request.on('response', (response) => {
response.on('data', (chunk) => {
request.write('')
request.end()
let body = JSON.parse(chunk)
info = body.data
if (info) {
resolve(info)
} else {
reject('Something went wrong');
}
});
});
})
}
getInfo()
.then(data => {
// this will be your info object
console.log(data)
})
.catch(err => {
// this will log 'Something went wrong' in case of any error
console.log(err)
})
You need to return inside your, on type event handler. Read more about asynchronous code and synchronous code here
I couldn't find the net module and the one which is included with Nodejs do not have request method. So to get the similar concept of event emiters and promise I am using http module and doing a http request to fetch json and parse it
'use strict'
var https = require('https');
const getInfo = async () => {
// create a new promise chain
// remember it is a chain, if one return is omitted
// then the chain is broken
return new Promise((resolve, reject) => {
var options = {
host: 'support.oneskyapp.com',
path: '/hc/en-us/article_attachments/202761727/example_2.json'
};
// start the request
https.request(options, function (response) {
var str = '';
// data arrives in chunks
// chunks needs to be stitched together before parsing
response.on('data', function (chunk) {
str += chunk;
});
// response body obtained
// resolve (aka return) the result
// or parse it, or do whatever you want with it
response.on('end', function () {
resolve(str)
});
// errors are another event
// listen for errors and reject when they are encountered
response.on('error', function (err) {
reject(err)
})
}).end()
})
}
//*********************************************
// using async await
//*********************************************
// if this is the entry point into app
// then top-level async approach required
(async ()=>{
try{
let data = await getInfo()
console.log("From ASYNC AWAIT ")
console.log(JSON.stringify(JSON.parse(data)))
}
catch (err) {
console.log("operation failed, error: ", err)
}
})();
//************************************************
// using promise chains
//************************************************
getInfo()
.then((data)=>{
console.log("FROM PROMISE CHAIN ")
console.log(JSON.stringify(JSON.parse(data)))
})
.catch((err)=>{
console.log("operation failed, error: ", err)
})
Tyr this, it might works for you,
let info;
const getInfo = async (_url)=>{
const response = await fetch(_url);
const data = await response.json();
info = data;
} ;
const url = "some url";
getInfo(url);
console.log(info);
Async function always returns a promise, so either consume that promise or internally await the data and assign it to some variable.
Check for the valid data required in info by logging it to the console.

Cannot read property 'code' of undefined from promise, context api (react hook)

I am making my first hybrid web app using react hook.
I am faced with a timing problem with promise and context api.
Here are the logic I am facing with.
A function fetchApplications gets data from firebase firestore, it is defined in globalcontext.js (called in tableView)
tableView.js calls fetchApplications in useEffect.
fetchApplications is defined as promise function, I expect it will resolve(return) data until data fetched, it will resolove(return) object like {code:200, data:data}
problem
in the fetchData.code,
Cannot read property 'code' of undefined
Here is my code
In the tableview.js
React.useEffect(() => {
context.fetchApplications(auth.userId, "")
.then(function (fetchData) {
console.log("Fetched data: ", fetchData); ///this is undefined
if (fetchData.code !== 200) { /// error part
alert(fetchData.msg);
}
if (!fetchData.data) {
alert("No applications");
}
setsData(fetchData.data);
});
}, []);
In the GlobalContext.js
async function fetchApplications(userId, role) {
return new Promise((resolve, reject) => {
// resolve({ code: 200, data: "data" }); //If I add this code, it will be alright.
let dataArray = [];
let applicationRef = db
.collection("Users")
.doc(userId)
.collection("Applications");
applicationRef
.get()
.then(function (qs) {
qs.forEach(function (doc) { //doesn't work
console.log(doc.id, " => ", doc.data());
console.log(doc.size, " => ", typeof doc);
dataArray.push(doc.data());
});
return Promise.resolve(dataArray);
})
.then((data) => {
console.log("Global, Fetched Data", dataArray);
if (data) {
resolve({ code: 200, data: data });
}
return;
});
}).catch(function (error) {
reject({ code: 400, msg: "시스템에러 고객센터에 문의해주세요" });
});
}
wrote in codesendbox
If I was write the wrong way of promise, please let me know.
thanks
You're implementing a couple of bad practices and have some major issues. For starters, fetchApplications is marked as async but you're returning a manually created promise which is quite overkill as your fetching code actually generates a promise which you should directly return. Furthermore:
.then(function (qs) {
qs.forEach(function (doc) { //doesn't work
console.log(doc.id, " => ", doc.data());
console.log(doc.size, " => ", typeof doc);
dataArray.push(doc.data());
});
return Promise.resolve(dataArray);
})
I am not sure what exactly "//doesn't work" should mean but return Promise.resolve(dataArray); won't cut it for you. You're already in the then chain, so you can't resolve anything from the main promise at this point. You should just pass the data to the next then callback as return dataArray;.
All in all, I will suggest ditching the then-ables syntax all together and migrate to async/await altogether:
async function fetchApplications(userId, role) {
try {
const dataArray = [];
const applicationRef = db
.collection("Users")
.doc(userId)
.collection("Applications");
const querySnapshot = await applicationRef.get();
querySnapshot.forEach(doc => {
dataArray.push(doc.data());
});
return {
code: 200,
data: dataArray
};
}
catch (error) {
return {
code: 400,
msg: '시스템에러 고객센터에 문의해주세요'
};
}
}
Then, in your react component/hook:
React.useEffect(() => {
const fetchApplicationDataAsync = async () => {
const result = await context.fetchApplications(auth.userId, "");
if (result.code !== 200) {
alert(result.msg);
}
if (!result.data) {
alert("No applications");
}
setsData(result.data);
}
fetchApplicationDataAsync();
}, [auth.userId]);
Another problem and bad practice is that you're not specifying your dependencies for the useEffect hook. You have 2 external dependencies: the auth.userId paramater and the ontext.fetchApplications function. We alleviate one of the problem by creating the fetch function in the body of useEffect itself. However, the auth.userId should go into the dependency array as stated above.
You have to check for fetchData to be defined before accessing its properties.
A short form would be
if (fetchData && fetchData.code !== 200){...}
Applied to your code:
React.useEffect(() => {
context.fetchApplications(auth.userId, "")
.then(function (fetchData) {
console.log("Fetched data: ", fetchData); ///this is undefined
if (fetchData && fetchData.code !== 200) { /// error part
alert(fetchData.msg);
}else {
alert("No applications");
}
setsData(fetchData.data);
});
}, []);
By calling then() on the fetchApplications() function, as follows, you pass to the callback the fullfilment value from the Promise returned by fetchApplications() (i.e. fetchData gets the value returned by fetchApplications()).
context.fetchApplications(auth.userId, "")
.then(function (fetchData) {...}
However, fetchApplications() returns a Promise that resolves with undefined because, actually, you don't return the Promises chain. This is why you get an error on fetchData.code.
Adapting fetchApplications() as follows (using await, since you use async) should do the trick (untested!):
async function fetchApplications(userId, role) {
try {
let dataArray = [];
let applicationRef = db
.collection('Users')
.doc(userId)
.collection('Applications');
const qs = await applicationRef.get();
qs.forEach(doc => {
console.log(doc.id, ' => ', doc.data());
console.log(doc.size, ' => ', typeof doc);
dataArray.push(doc.data());
});
return { code: 200, data: dataArray };
} catch (error) {
return { code: 400, msg: '시스템에러 고객센터에 문의해주세요' };
}
}
Note that in any case you return an object with a code property, so no more problem when doing fetchData.code.

Save Async/Await response on a variable

I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});

Categories

Resources