Tell us what’s happening:
As you can see in this image, the fetch_stockdata() function returns ‘undefined’.
The reason it does this seems to be because the fetch_stockdata() function returns the value BEFORE the findOne() method and the code inside it is complete.
So the order I believe the program is running the code is:
- Run
fetch_stockdata()
- Within
fetch_stockdata() run fetch_price()
return stockdata_obj (which is undefined at the moment)
- Within
fetch_stockdata() run db_model.findOne() method
What I want the program to do instead is:
- Run
fetch_stockdata()
- Within
fetch_stockdata() run fetch_price()
- Within
fetch_stockdata() run db_model.findOne() method
return stockdata_obj (which is now not undefined as it has been filled by the db_model.findOne() method)
How can I fix this?
Your project link(s)
solution: https://replit.com/@jaimeggb/boilerplate-project-stockchecker-4
Your browser information:
User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36
Challenge: Stock Price Checker
Link to the challenge:
If fetch_stockdata returns a promise then you can use an async function and await it.
If it does not return a promise then make it into one:
let stockdata_obj = new Promise(res => res(jfunc.fetch_stockdata(
req.query.stock,
req.query.like,
req.ip,
Stock
)))
stockdata_obj.then(data => console.log(`stock data: ${data}`));
You will need to use res.json in a subsequent .then callback if you want it to chronologically happen afterwards
Hi @caryaharper
I tried your code and got practically the same result as before:
Have I implemented the code wrong or what could be going on?
Thanks for your time
can you show me the definition for fetch_stockdata
I have pasted my ‘Jfuncs.js’ file below, which includes ‘fetch_stockdata()’. You can find everything put together in this link:
solution: https://replit.com/@jaimeggb/boilerplate-project-stockchecker-4
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
class Jfuncs {
fetch_price(stock_symbol) {
let xhr = new XMLHttpRequest();
let price;
let requestURL = 'https://stock-price-checker-proxy.freecodecamp.rocks/v1/stock/' + stock_symbol + '/quote';
xhr.open('GET', requestURL, false);
xhr.onload = function () {
price = JSON.parse(xhr.responseText).latestPrice;
};
xhr.send();
return price
}
fetch_stockdata(stock_symbol, like_box_checked, user_ip, db_model) {
// Fetch price
let price = this.fetch_price(stock_symbol);
console.log('price', price);
// Fetch likes and return price and likes in variable object 'stockdata_obj'
db_model.findOne({ stock_symbol: stock_symbol }, function(err, stockFound_1) {
console.log('Like box checked? ', like_box_checked)
if(err){
return console.log(err);
};
switch(true) {
case ((stockFound_1 != null) && (like_box_checked == 'true') && (stockFound_1.ips_that_like_it.includes(user_ip) == true)): {
// 1 Return stockdata as is
console.log('case 1');
let likes_for_that_stock = stockFound_1.ips_that_like_it.length
let stockdata_obj = {"stockData":{"stock":stock_symbol,"price":price,"likes":likes_for_that_stock}};
return stockdata_obj;
break;
}
case ((stockFound_1 != null) && (like_box_checked =='true') && (stockFound_1.ips_that_like_it.includes(user_ip) != true)): {
// 2 Update stock document/record to append new IP and increase likes +1
console.log('case 2');
Stock.findByIdAndUpdate(
stockFound_1._id,
{ $push: {ips_that_like_it: user_ip} },
{new: true},
//callback (show response object here)
function(err, stockUpdated_1) {
if(err){
return console.error(err);
}else{
let likes_for_that_stock = stockUpdated_1.ips_that_like_it.length
let stockdata_obj = {"stockData":{"stock":stock_symbol,"price":price,"likes":likes_for_that_stock}};
return stockdata_obj;
};
}
);
break;
}
case ((stockFound_1 != null) && (like_box_checked != 'true') && (stockFound_1.ips_that_like_it.includes(user_ip) == true)): {
// 3 Return stockdata as is
console.log('case 3');
let likes_for_that_stock = stockFound_1.ips_that_like_it.length
let stockdata_obj = {"stockData":{"stock":stock_symbol,"price":price,"likes":likes_for_that_stock}};
console.log('this has run');
console.log(stockdata_obj);
return stockdata_obj;
break;
}
case ((stockFound_1 != null) && (like_box_checked != 'true') && (stockFound_1.ips_that_like_it.includes(user_ip) != true)): {
// 4 Return stockdata as is
console.log('case 4');
let likes_for_that_stock = stockFound_1.ips_that_like_it.length
let stockdata_obj = {"stockData":{"stock":stock_symbol,"price":price,"likes":likes_for_that_stock}};
return stockdata_obj;
break;
}
case ((stockFound_1 == null) && (like_box_checked == 'true') /* && stockFound_1.ips_that_like_it.includes(user_ip) != true */): {
// 5 Create new stock document/record in DB and return stock data
console.log('case 5');
const createAndSaveStock = function(done) {
new Stock({
stock_symbol: stock_symbol,
ips_that_like_it: [user_ip],
likes_for_that_stock: 1
}).save(function(err, stock_created_1) {
console.log(stock_created_1);
if(err){
return console.error(err);
}else{
//Show client the stock_symbol and id pair created and saved
let stockdata_obj = {"stockData":{"stock":stock_symbol,"price":price,"likes":likes_for_that_stock}};
return stockdata_obj;
done(null,stock_created_1);
};
});
};
createAndSaveStock(() => {});
break;
}
case ((stockFound_1 == null) && (like_box_checked != 'true') /* && stockFound_1.ips_that_like_it.includes(user_ip) != true */): {
// 6 Return stockdata as is
console.log('case 6', like_box_checked);
let stockdata_obj = {"stockData":{"stock":stock_symbol,"price":price,"likes":likes_for_that_stock}};
return stockdata_obj;
break;
}
}; // closes 'switch (true) '
}); // closes 'db_model.findOne({ stock_symbol: stock_symbol }, function(err, stockFound_1) '
} // closes 'fetch_stockdata(stock_symbol, like_box_checked, user_ip, db_model) '
}
module.exports = Jfuncs;
I have found a clean solution others may benefit from:
'use strict';
//Install and set up mongodb and mongoose
const mongodb = require('mongodb');
const mongoose = require('mongoose');
const mySecret = process.env['MONGO_URI']
const Jfuncs = require('../controllers/Jfuncs.js');
module.exports = function (app) {
// This variable creates an instance of the class Jfuncs (i.e. creates an object) when called
let jfunc = new Jfuncs();
//Connect mongoose to mongodb database
mongoose.connect(mySecret, { useNewUrlParser: true, useUnifiedTopology: true });
//Create a schema and a Model for data going into mongodb database
const { Schema } = mongoose;
const stockSchema = new Schema({
stock_symbol: {type: String, required: true},
ips_that_like_it: {type: [String], required: true},
likes_for_that_stock: {type: Number, required: true}
});
const Stock = mongoose.model('Stock', stockSchema);
// What must happen when we click the first 'Get Price!' button
app.route('/api/stock-prices')
.get(async function (req, res){
if (typeof req.query.stock == 'string') {
// 1 stock - Get single price and total likes
let stockFound_1 = await Stock.findOne({ stock_symbol: req.query.stock }).exec();
console.log(stockFound_1);
let stockdata_obj = jfunc.fetch_stockdata(
req.query.stock,
req.query.like,
req.ip,
stockFound_1
);
return res.json(stockdata_obj);
} else if(typeof req.query.stock == 'object' /* In JS, an array is type object*/) {
// 2 stocks - Compare and get relative likes
// Get first stock object
let stockFound_1 = await Stock.findOne({ stock_symbol: req.query.stock[0] }).exec();
let stockdata_obj_1 = jfunc.fetch_stockdata(
req.query.stock[0],
req.query.like,
req.ip,
stockFound_1
);
console.log(stockdata_obj_1);
// Get second stock object
let stockFound_2 = await Stock.findOne({ stock_symbol: req.query.stock[1] }).exec();
let stockdata_obj_2 = jfunc.fetch_stockdata(
req.query.stock[1],
req.query.like,
req.ip,
stockFound_2
);
console.log(stockdata_obj_2);
// Join the two in a single object to show user relative likes
let stockdata_objs_compared = jfunc.stockdata_comparison_obj_creator(stockdata_obj_1,stockdata_obj_2);
return res.json(stockdata_objs_compared);
}; // closes 'else if'
}); // closes '.get route'
}; // closes 'module.exports'
The keys to the solution are: using ‘async’ and ‘await’, and retrieving records from mongoDB using the following simple line of code, instead of complicated callback functions:
let stockFound_1 = await Stock.findOne({ stock_symbol: req.query.stock }).exec();