Clearner way to wait for function to complete before continuing - javascript

First I fetch a random quote from a public api. Only AFTER I actually have a quote I want to render the page including that quote.
I am a JavaScript newbie so please bear with me if this is a stupid question.
I was struggling with waiting for the api call to return BEFORE continuing with rendering the page.
I wanted to do the following, but this doesn't work because res.render will be called before I have a quote. (note: I am using Express and Axios)
async function getRandomQuote() {
try {
const res = await axios.get("https://api.quotable.io/random?tags=famous-quotes")
console.log(`${res.data.content} - ${res.data.author}`) //This prints fine
return res.data
} catch(e) {
console.log('error', e)
}
}
app.get('/', (req, res) => {
const quote = getRandomQuote()
console.log(`${quote.content} - ${quote.author}`) //This prints 'undefined' because 'getRandomQuote' isn't finished yet
res.render('home', { quote })
})
The only way I figured out to do it is as follows, but I find this really messy.
Is there a cleaner way to do this? Or do I always need to put all the lines of code that I want to wait for each other in an async function?
async function getRandomQuote() {
try {
const res = await axios.get("https://api.quotable.io/random?tags=famous-quotes")
console.log(`${res.data.content} - ${res.data.author}`) //This prints fine
return res.data
} catch(e) {
console.log('error', e)
}
}
app.get('/', (req, res) => {
const getQuoteAndRender = async() => {
const quote = await getRandomQuote()
console.log(`${quote.content} - ${quote.author}`) //This prints only if I wrap everything in yet another aync function, otherwise it will not wait for 'getRandomQuote' to complete
res.render('home', { quote })
}
getQuoteAndRender()
})
(Note: I realize rendering the page after I successfully get a quote is not ideal either because this means I will not get a page at all if the quote api (for some reason) doesn't work. But for now I just want to know how to do this with the waiting.)

Heres what you need to do. Make your controller async by doing this app.get('/', async (req, res)
app.get('/', async (req, res) => {
const quote = await getRandomQuote()
console.log(`${quote.content} - ${quote.author}`) //This prints 'undefined' because 'getRandomQuote' isn't finished yet
res.render('home', { quote })
})

Try this:
getRandomQuote()
.then(quote => {
console.log(`${quote.content} - ${quote.author}`)
res.render('home', { quote })
});

Related

Res.send sends an empty object, even though console.log shows it isn't empty?

I am trying to use the google-sheets api with express and don't have much experience with javascript. I'm attempting to use pass a json object from express to react, but it seems that whenever I finally send the object, it just renders as empty on the frontend?
I've tried using res.body/res.data, but the object doesn't seem to have either. I've also tried to put as many awaits as I can everywhere to make sure the object is loaded in before sending, but nothing seems to do the trick. If I use res.json or res.send with just the response object, I get a circular structure converting to JSON error. Here is the code I'm working with.
async function docShit() {
// Initialize the sheet - doc ID is the long id in the sheets URL
const doc = new GoogleSpreadsheet(
"--SPREADSHEET ID--"
);
// Initialize Auth - see https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication
await doc.useServiceAccountAuth({
// env var values are copied from service account credentials generated by google
// see "Authentication" section in docs for more info
client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY,
});
await doc.loadInfo(); // loads document properties and worksheets
const sheet = doc.sheetsByTitle[--WORKSHEET TITLE--];
const rows = await sheet.getRows(); // can pass in { limit, offset }
return rows;
}
app.get("/home", async (req, res) => {
try {
await docShit()
.then((response) => {
res.send(Promise.resolve(response)); //console log shows the object, but res.send just sends nothing??
})
.catch((err) => console.log(err));
} catch (err) {
console.error(err.message);
}
});
There is no res.send at all in your code. Also, you use await and .then together, but I consider them alternatives. Try the following:
app.get("/home", async (req, res, next) => {
try {
var response = await docShit();
console.log(response);
/* If response is circular, decide which parts of it you want to send.
The following is just an example. */
res.json(response.map(function(row) {
return {id: row.id, cells: row.cells.map(function(cell) {
return {id: cell.id, value: cell.value};
};
})};
} catch (err) {
console.error(err.message);
next(err);
}
});

javascript:how does it work when a function returns another function and accepts another function as parameters

I followed the youtube Node & Express project tutorial and Im confused facing these code:
This is in async js file:
const asyncHWrapper = (fn) => {
return async (req, res, next) => {
try {
await fn(req, res, next);
} catch (error) {
next(error);
}
};
};
module.exports = asyncHWrapper;
And this is the usage:
const Task = require("../models/taskModel");
const asyncWrapper = require("../middleware/async");
const { createCustomError } = require("../errors/customErrors");
const getAllTasks = asyncWrapper(async (req, res) => {
const tasks = await Task.find({});
res.status(200).json({ tasks });
});
Im just confused about these questions:
Does is necessary to return an arrow function in the asyncWrapper? Why doesn't just call the function?
Where do the params (req,res) in the asyncWrapper function come from? In the "fn" function declaration?
Why should I write two pairs of async and await in the wrapper and when I call it?
Thanks a lot!
The youtube tutorial link :Node.js / Express Course - Build 4 Projects
Let me analyze the code first, and then I'll answer your question.
So here is your wrapper
const asyncHWrapper = (fn) => {
return async (req, res, next) => {
try {
await fn(req, res, next);
} catch (error) {
next(error);
}
};
};
and here is how it is used
const getAllTasks = asyncWrapper(async (req, res) => {
const tasks = await Task.find({});
res.status(200).json({ tasks });
});
the asyncWrapper accept an fn param, in this case, it is this function:
async (req, res) => {
const tasks = await Task.find({});
res.status(200).json({ tasks });
}
after asyncHWrapper is called with the above function, it will return another function, in this case, the return function is assigned as the name getAllTasks.
Now for your question:
Does is necessary to return an arrow function in the asyncWrapper? Why doesn't just call the function?
Well basically you can write it like this
const asyncHWrapper = async (fn, req, res, next) => {
try {
await fn(req, res, next);
} catch (error) {
next(error);
}
};
And call it like this
await asyncHWrapper(async (req, res) => {
const tasks = await Task.find({});
res.status(200).json({ tasks });
}, req, res, next)
But in this case, it's just a normal function with callback, it's not a wrapper, and its name shouldn't be asyncHWrapper.
Where do the params (req,res) in the asyncWrapper function come from? In the "fn" function declaration?
No, it comes from getAllTasks, your fn is just consuming two values (req, res), and the next param will be used for error handling. So when you call getAllTask, you must pass in three params like this getAllTasks(req, res, next)
Why should I write two pairs of async and await in the wrapper and when I call it?
I'm not sure what you meant by "two pairs of async and await". I assume you're referring to await when calling getAllTasks and await when calling fn?
That's just because both of them are async functions.
I hope that this answer can help you think about the concept of a "wrapper". Let's say we have a function divide:
const divide = (a,b) => a/b;
You can use this in normal code quite easily:
x = divide(10,5); // sets x to 2.
But you may decide that you care about the possibility of errors. In this case, division by zero is a possibility. You could certainly include some error handling code where you define the divide function. But you could also choose to "wrap" divide with an error handler. This way, we can keep the error handling aspects away from the main division logic. We would like to be able to define a safeDivide function like this:
const safeDivide = catchErrors(divide);
In the same way that divide is a function that takes two arguments, safeDivide also has to be a function that takes two arguments. So the catchErrors wrapper will have to return a function. We will start with something like this:
const catchErrors = (fn) => {
return (p,q) => fn(p,q);
}
If you pass a function fn to catchErrors, it will return a function. That returned function takes two arguments p and q, and returns fn(p,q). So far, this doesn't really achieve anything (except limiting the number of arguments). But now, we can add a try/catch block. I'll do it in a couple of steps, because the notation can be confusing.
The first step is to put an explicit return inside the inner arrow function.
const catchErrors = (fn) => {
return (p,q) => {
return fn(p,q);
}
}
This is technically the same code - it just looks slightly different. Now we add the try/catch.
const catchErrors = (fn) => {
return (p,q) => {
try {
return fn(p,q);
} catch (e) {
console.log("Error occurred. Continuing with null result.");
return null;
}
}
}
So now, the function returned from catchErrors will do the same as the original function, except when an exception is thrown, in which case it will return null. (I'm not saying that this is the best general way to handle exceptions, but it's useful as an example, and it's related to the original question.)
So now look again at where we use this catchErrors wrapper function.
const safeDivide = catchErrors(divide);
When you call the wrapper catchErrors with function divide, it doesn't actually do any dividing - it doesn't yet have any numbers to divide. Instead, it builds up a new function that, whenever it is called, would do the dividing, and catch any exception that arises. I hope that answers your first question.
Your second question is where req and res come from. They are names given to arguments that will be passed to the function. They can be passed to the wrapped function (along with a 3rd argument next), and they will also be passed to the inner (nameless) function which includes calls to Task.find and res.status(200). Those arguments will be provided by the Express (or other) web framework.
I will leave your 3rd question, on the async/await aspects of the wrapper, for another answer.

Node.js and express.js how to read 'get' endpoint?

I am trying to pass simple data from my server to a javascript file called on another html page. I am testing sending a single string from the server, but am not sure how to receive it. Server below:
const express = require('express')
const app = express()
const port = 3000
app.use(express.static("./assets"));
app.get('/', (req, res) => {
//res.send('Hello World!')
res.sendFile('./main.html', { root: __dirname });
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
app.get('/get-test', async (_req, res) => {
try {
const test_string = "get-test request string";
return res.send(test_string);
} catch (e) { throw e; }
});
And in another javascript file I have the following:
async function testing() {
const response = await fetch('/get-test');
console.log(response);
}
testing();
The console.log gives me a whole object, and clicking through it I can't seem to find my test_string anywhere:
So I believe the get request worked, but how do I actually access the data inside that I want?
You need to call await response.text() before console.loging it.
So in this functio:
async function testing() {
const response = await fetch('/get-test');
console.log(response);
}
testing();
You will need to put await inside the console.log
so like this:
async function testing() {
const response = await fetch('/get-test');
console.log(await response);
}
testing();
Why ?
Since javascript is asynchronous when retrieving data there will be a delay. That is why they implemented async / await and promises. So when you trying to make a get request with fetch you will need need to await the response. But the response is also a promise, which mean you will need to await the response aswell. This makes more sense, when you try to process the response to lets say json.
A little tip
When the console.log returns no error, but no data either. It might because of a promise issue

Browser tab keeps loading after express js get method

I'm trying to build a simple REST API using Express.js, first time using it.
So I run the Express application generator using npx express-generator. Then, on /routes/users.js, I add a get method to retrieve data from an MSSQL database with the following code:
router.get('/api/v1/test', (req, res) => {
(async () => {
try {
let pool = await sql.connect(dbConfig);
let result1 = await pool.request().query('select * from andromedadb.dbo.test');
console.dir(result1);
} catch (err) {
console.log('err', err);
}
})();
});
I know the connection is successful because the console prints the following:
{
recordsets: [ [ [Object] ] ],
recordset: [ { id: 1, name: 'hello', age: 17 } ],
output: {},
rowsAffected: [ 1 ]
}
My issue is that the browser tab keeps loading and executing the get method over and over.
I also tried the code snippet on https://www.js-tutorials.com/nodejs-tutorial/simple-example-of-nodejs-express-with-mssql/#Listing_Nodejs_MSSQL_Using_Express, but the very same thing was happening.
Thanks in advance.
You need to send the response back to the request. I just modified your code, Look at the changes in the below code.
router.get('/api/v1/test', (req, res) => {
(async () => {
try {
let pool = await sql.connect(dbConfig);
let result1 = await pool.request().query('select * from andromedadb.dbo.test');
console.dir(result1);
res.status(200).send(result1);
} catch (err) {
res.status(401).send(err);
console.log('err', err);
}
})();
});
You need to send back some response with your res object or call the next() function to go into your next middleware, otherwise you are just leaving your request hanging, thus the infinite load. Add a res.send() or res.json() function to mark the end of your function.
router.get('/api/v1/test', (req, res) => {
(async () => {
try {
let pool = await sql.connect(dbConfig);
let result1 = await pool.request().query('select * from andromedadb.dbo.test');
console.dir(result1);
res.status(200).send("Received the response successfully");
} catch (err) {
console.log('err', err);
res.status(500).send("Something went wrong");
}
})();
});
How are you calling the route from the front end application?
As you are using an async function for your get method, remember to use 'await':
const response = await getRecordSet();
// do stuff with 'response'

Synchronous and asynchronous vs callback in mongoose

hi guys i am new in node and have this simple question , what is difference between this two sniped
Note: i know async / await functionality and also in front-end application it is best practice for handle async action but in node in working with mongoose i want know which approach better for handle
first solution
// for example we pass this function as controller to route handler
exports.myController = async (req, res, next) => {
try {
const data = await Model.find();
const some = await new Model(data).save();
} catch(e) {
next(e);
}
}
second solution
exports.myController = (req, res, next) => {
const data = Model.find((err, data_) => {
const some = new Model(data_);
some.save((err, result) => {
....
})
});
}
i want to know when i have an error from mongoose , in second way can throw error like this
// in callback function
if (err) {
throw Error();
}
but how can i handle this in async/await solution
you simply throw it or log it in your catch block:
try {
const data = await Model.find();
const some = await new Model(data).save();
} catch(e) {
throw e;
next(e);
}
the async/await is working like promises but without no nesting callbacks and it throws an error synchronous to what errors first and stop from executing the other lines.
Based in your edited Note:
you should always go for the async solution way in nodejs or even for anything related to javascript it's the best composable and reusable solution.

Categories