I'm new with Typescript/JS and I'm coming from Java.
I am trying to solve a scenario wherein the 2nd method call is dependent on 1st method's return value.
I managed to create a getCustomerNumber() method that is able to return a string value instead of undefined The 2nd method getCustomerName() needs the return value of getCustomerNumber() as argument for it to successfully return the customer name value.
I tried making both of them as async methods that returns a Promise<string> object. However, getCustomerName() fails to retrieve a customer number from getCustomerNumber()
private async getCustomerNumber(rorn : string): Promise<string> {
const getCustNo = new Request();
getCustNo.transaction = "GetCustNum";
getCustNo.argument = rorn;
let customerNo;
await Service.execute(getCustNo).then((response: IResponse) => {
customerNo = response.items[0]["CustNum"];
}).catch((response: IResponse) => {
this.log.Error(response.errorMessage);
});
return customerNo;
}
private async getCustomerName(cuno: string) : Promise<string> {
const getCustName = new Request();
getCustName.transaction = "GetCustName";
getCustName.argument = cuno;
let customerName;
await Service.execute(getCustName).then((response: IResponse) => {
customerName = response.items[0]["CustName"];
}).catch((response: IResponse) => {
this.log.Error(response.errorMessage);
});
return customerName;
}
private async callMethodsAndSetLabelText() : Promise <void> {
const orderNum = "123456";
const customerNumber = await this.getCustomerNumber(orderNum).trim());
const customerName = await this.getCustomerName(customerNumber) ; //get customer name by number
console.log(customerName);
const labelElement = new LabelElement();
labelElement.setText(customerName);
}
Calling the methods:
callMethodsAndSetLabelText()
Logging the customer name in callMethodsAndSetLabelText() results to undefined
console.log(customerName); //undefined
I don't know how to set it up in a way that will work for the 2nd method since it's dependent on the 1st method.
I'd appreciate any comments or suggestions.
Thank you
Related
I'm currently working on something where I need to get async data and push it to an array. Please see below.
const getStudent = async (id: string) => {
const student = await fetchSomeData(id);
const markedEssayArray: string[] = [];
for await(const essayName of student.essay) {
const markedEssay = await fetchMarkedEssay(essayName);
markedEssayArray.push(markedEssay)
};
student.essay = markedEssayArray;
return student;
};
so basically, I fetch a student, and update the student.essay array with the marked versions. However when I test the code, the student.essay array returns an empty array [].
Could anyone kindly advise where I went wrong?
You can try like this :
const getStudent = async (id: string) => {
const student = await fetchSomeData(id);
let markedEssayArray: string[] = [];
const essayArr = student.essay;
essayArr.forEach((singleEssay) => {
let markedEssay = await fetchMarkedEssay(essayName);
markedEssayArray.push(markedEssay)
}
student.essay = [...markedEssayArray];
return student;
};
I have the following code:
const readDataFromSql = () => {
// going to have to iterate through all known activities + load them here
let sql = "[...]"
return new Promise((resolve, reject) => {
executeSqlQuery(sql).then((dict) => {
let loadedData = [];
for (let key in dict) {
let newItemVal = new ItemVal("reading hw", 7121, progress.DONE);
loadedData.push(newItemVal);
}
resolve(loadedData);
});
});
}
ItemVal implementation:
class ItemVal {
constructor(name, time, type) {
this.name = name
this.time = time
this.type = type
}
}
Let's assume that newItemVal = "reading hwj", 5081, progress.PAUSED when readDataFromSql() first runs.
readDataFromSql() is then again called after some state changes -- where it repulls some information from a database and generates new values. What is perplexing, however, is that when it is called the second time, newItemVal still retains its old properties (attaching screenshot below).
Am I misusing the new keyword?
From what I can see in your example code, you are not mutating existing properties but creating a new object with the ItemVal constructor function and adding them to an array, that you then return as a resolved promise. Are you sure the examples you give a correct representation of what you are actually doing
Given that, I'm not sure what could be causing the issue you are having, but I would at least recommend a different structure for your code, using a simpler function for the itemVal.
Perhaps with this setup, you might get an error returned that might help you debug your issue.
const itemVal = (name, time, type) => ({ name, time, type })
const readDataFromSql = async () => {
try {
const sql = "[...]"
const dict = await executeSqlQuery(sql)
const loadedData = dict.map((key) =>
ItemVal("reading hw", 7121, progress.DONE)
)
return loadedData
} catch (error) {
return error
}
};
If the issue is not in the function, then I would assume that the way you handle the data, returned from the readDataFromSql function, is where the issue lies. You need to then share more details about your implementation.
const readDataFromSql = async () => {
let sql = "[...]"
------> await executeSqlQuery(sql).then((dict) => {
Use the await keyword instead of creating a new promise.
I did some modification and found that below code is working correctly, and updating the new values on each call.
const readDataFromSql = () => {
return new Promise((resolve, reject) => {
let loadedData = [];
let randomVal = Math.random();
let newItemVal = new ItemVal(randomVal*10, randomVal*100, randomVal*1000);
loadedData.push(newItemVal);
resolve(loadedData);
});
}
Could you recheck if you are using below line in the code, as it will instantiate object with same properties again and again.
let newItemVal = new ItemVal("reading hw", 7121, progress.DONE);
You can modify your code as below to simplify the problem.
const readDataFromSql = async () => {
// going to have to iterate through all known activities + load them here
let sql = "[...]" // define sql properly
let result = await executeSqlQuery(sql);
let loadedData = [];
for (let row in result) {
let newItemVal = new ItemVal(row.name, row.time, row.type);
loadedData.push(newItemVal);
}
return loadedData;
}
class ItemVal {
constructor(name, time, type) {
this.name = name
this.time = time
this.type = type
}
}
What you are talking about is an issue related to Object mutation in Redux, however, you didn't add any redux code. Anyway, you might be making some mistake while recreating(not mutating) the array.
General solution is the use spread operator as:
loadedData = [ ...loadedData.slice(0) , ...newloadedData]
In Dropdown.js line 188 instead of console.log-ing your variable write debugger;
This will function as a breakpoint. It will halt your code and you can inspect the value by hovering your mouse over the code BEFORE the newItemVal is changed again.
I can see in your screenshot that the newItemVal is modified again after you log it.
First I have the following code section:
export default class Recipe {
constructor(recID) {
this.RecipeID = recID;
}
async GetRecipe() {
try {
let Result = await Axios(
`https://forkify-api.herokuapp.com/api/get?rId=${this.RecipeID}`
);
this.Tilte = Result.data.recipe.title;
this.Author = Result.data.recipe.publisher;
this.Image = Result.data.recipe.image_url;
this.Url = Result.data.recipe.source_url;
this.Ingredients = Result.data.recipe.ingredients;
this.PublisherUrl = Result.data.recipe.publisher_url;
this.Rank = Result.data.recipe.social_rank;
} catch (error) {
alert(error);
}
}
CalculateTime() {
try {
this.Time = Math.ceil(this.Ingredients.length / 3) * 15; // error is here
} catch (error) {
console.log(this.RecipeID + ": Length Error->"+error);
}
}
}
Then I call the above code in a separate file like:
import Recipe from "./Recipe";
const RecipeController = async () => {
const ID = window.location.hash.replace("#", "");
if (ID) {
AppState.Recipe = new Recipe(ID);
try {
await AppState.Recipe.GetRecipe();
AppState.Recipe.CalculateTime();
console.log(AppState.Recipe);
} catch (error) {
alert(error);
}
}
};
Now as shown in the following image, that I do get the response of the request & promised is resolved plus there are elements in the 'ingredients' array but sometimes I still get the error "cannot read property 'length' of undefined" when I call the 'CalculateTime()' although the array is now defined and sometimes I don't get any error & it works perfectly fine.Why this random behavior? Even the IDs in the JSON response & the error I logged match i.e. 47746.
This is one reason why having too many try/catches can obscure the causes of errors, making debugging difficult. The problem can be reduced to the following:
class Recipe {
constructor(recID) {
this.RecipeID = recID;
}
async GetRecipe() {
let Result = await fetch(
`https://forkify-api.herokuapp.com/api/get?rId=47746`
).then(res => res.json());
console.log(Result); // <----- look at this log
this.Tilte = Result.data.recipe.title;
// on the above line, the error is thrown
// Cannot read property 'recipe' of undefined
}
}
const r = new Recipe();
r.GetRecipe();
See the log: your Result object does not have a .data property, so referencing Result.data.recipe throws an error. Try Result.recipe instead:
class Recipe {
constructor(recID) {
this.RecipeID = recID;
}
async GetRecipe() {
let Result = await fetch(
`https://forkify-api.herokuapp.com/api/get?rId=47746`
).then(res => res.json());
const { recipe } = Result;
this.Tilte = recipe.title;
this.Author = recipe.publisher;
this.Image = recipe.image_url;
this.Url = recipe.source_url;
this.Ingredients = recipe.ingredients;
this.PublisherUrl = recipe.publisher_url;
this.Rank = recipe.social_rank;
}
CalculateTime() {
this.Time = Math.ceil(this.Ingredients.length / 3) * 15; // error is here
console.log('got time', this.Time);
}
}
(async () => {
const r = new Recipe();
await r.GetRecipe();
r.CalculateTime();
})();
Unless you can actually handle an error at a particular point, it's usually good to allow the error to percolate upwards to the caller, so that the caller can see that there was an error, and handle it if it can. Consider changing your original code so that RecipeController (and only RecipeController) can see errors and deal with them - you can remove the try/catches from the Recipe.
Are you sure some of the responses are not missing ingredients? And calculateTime is always called after getRecipe?
I would add a condition or fallback to prevent errors, as in.
this.Time = Math.ceil((this.Ingredients || []).length / 3) * 15;
I would speculate that this "random" behavior can be related to asynchronous code. You need to ensure that the class has Ingredients in place before you calculate. I have a feeling that you should try changing your syntax to a Promise handling using .then() and .catch(), especially since you already use try/catch in your code. This approach will ensure proper resolution of Promise on axios request, and will eliminate "randomness", because you will have a better control over different stages of Promise processing.
let Result = await Axios(
`https://forkify-api.herokuapp.com/api/get?rId=${this.RecipeID}`
)
.then((data) => {
this.Tilte = data.data.recipe.title;
this.Author = data.data.recipe.publisher;
this.Image = data.data.recipe.image_url;
this.Url = data.data.recipe.source_url;
this.Ingredients = data.data.recipe.ingredients;
this.PublisherUrl = data.data.recipe.publisher_url;
this.Rank = data.data.recipe.social_rank;
this.Ingerdients = data.data.recipe.ingredient;
}
.catch((err) => {
console.log(err);
return null;
});
I am getting JSON from a api GET call. I am trying to get a value of a item which I think is a array. I am trying to console log the low price of the json.
I tried to cycle through it like a array like open.openDate.btcusd[5] and so on.
//JSON DATA FROM API
btcusd":{
"high":"9206.36",
"low":"8804.57",
"volume":"1291.122483",
"last":"8989.64",
"bid":"8987.88",
"ask":"8998.24"
//Call
coin.getOpen()
.then(data=>{
coin.ui(data);
});
//Function
async getOpen(){
const openres = await
fetch(`https://api.lakebtc.com/api_v2/ticker`);
const openBtc = await openres.json();
return {
openDate : openBtc
}
}
//New Function to console.log
ui(open){
console.log(open.openDate.btcusd); //I want the low value
}
do call this function like
async function getOpen(){
const openres = await fetch(`https://api.lakebtc.com/api_v2/ticker`);
const openBtc = await openres.json();
// console.log(openBtc.btcusd.low);
return {
openDate : openBtc
}
}
// function call
getOpen()
.then(res => console.log(res.openDate.btcusd.low))
.catch(err => console.error(err))
Didnt completely get your requirement here, but the response here is an object not an array.
We can convert to array and print the low values for different entries like :
async function getOpen(){
const openres = await fetch(`https://api.lakebtc.com/api_v2/ticker`);
const openBtc = await openres.json();
return {
openDate : openBtc
}
};
getOpen().then(data=>{
ui(data);
});
function ui(obj){
var arr = Object.entries(obj.openDate);
var lowValues = arr.map(d => console.log(d[0] + " value of low is " + d[1].low));
}
I using Express with Node to interact with a MySQL database.
The create method of my Card object will save the data to the database and should update the object's id to be the insertId returned from the query.
class Card {
constructor(d) {
this.id = d.id || 0;
this.playerId = d.playerId;
this.drawn = false;
this.title = d.title
this.qty = d.qty;
this.hint = d.hint;
this.type = d.type;
this.descr = d.descr;
this.pair = d.pair
this.data = d;
}
create(conn) {
return new Promise( (resolve, reject) => {
var query = "INSERT INTO cards SET ? ";
conn.query(query, this.data, function(err, result) {
if (err) throw err;
(typeof result.insertId === 'number')
? resolve(result.insertId)
: reject("Failed to insert a new Card.");
resolve(result.insertId);
})
}).then( (id) => {
this.id = id;
console.log("Your insert id is");
console.log(this.id) // GREAT! It prints the correct id
return this; // Set the value of the promise to this Card instance
})
}
}
After creating a card, it should print the json representation of the card.
router.post('/create-card', function(req, res) {
var data = req.body;
var card = new Card(data);
card = card.create(db.connection);
res.json(card);
})
But the id of the returned object is always the default value (0).
I have played with a few different ways of trying to capture the id, set the object's id property with the new value, and persist that value to the object. However, it always seems to be a more locally scoped version of the card that gets updated, not the original card (which is printed to the page).
When using Promises, what is the proper place to assign object data and return that instance of the object?
You're calling res.json too soon. You need to await the promise's resolution:
card.create(db.connection).then( _ => res.json(card));
You are doing an async operation in create method but dont use it properly. You have to do one of two things:
Return promise from create, then wait for it to fulfilled with 'then' in your post route.
Use await and wait for the promise to be resolved.
In your code for now you are running in your router synchronize code while your create operation is async...