how to wait for the execution of a function - node.js

I have this repository , it should get the message by id :
findOne(id: number) {
return readFile('messages.json', 'utf8', (err, file: string) => {
const messages = JSON.parse(file);
return messages[id];
});
}
this is the service :
findOne(id: number) {
return this.messagesRepo.findOne(id);
}
and this is the controller
#Get('/:id')
getMessage(#Param() param: GetMessageDTO) {
const message = this.messagesService.findOne(param.id);
if (message == null) {
throw new NotFoundException('message not found');
}
return message;
}
I want the controller to wait for the execution of the function but I can't use async/await because readFile doesn't return promise , what should I do?

instead of readFile from fs, you can use the one from promises API of FS module like so:
import { readFile } from 'fs/promises'
// ...
async findOne(id: number): Promise<any> {
const jsonFileTxtBuffer = await readFile('messages.json');
const messages = JSON.parse(jsonFileTxtBuffer);
return messages[id];
}
Don't forget the await on calling .findOne(), of course.
pretty basic JS and node.js stuff not related with NestJS :)

Related

How to call a custom function synchronously in node js?

I am developing a server project which needs to call some functions synchronously. Currently I am calling it in asynchronous nature. I found some similar questions on StackOverflow and I can't understand how to apply those solutions to my code. Yet I tried using async/await and ended up with an error The 'await' operator can only be used in an 'async' function
Here is my implementation
function findSuitableRoom(_lecturer, _sessionDay, _sessionTime, _sessionDuration, _sessionType){
let assignedRoom = selectRoomByLevel(preferredRooms, lecturer.level, _sessionDuration); <------- Need to be call synchronously
if (!assignedRoom.success){
let rooms = getRooms(_sessionType); <------- Need to be call synchronously
assignedRoom = assignRoom(_lecturer.rooms, _sessionDuration, _lecturer.level);
} else {
arr_RemovedSessions.push(assignedRoom.removed)
}
return assignedRoom;
}
function getRooms(type){
switch (type){
case 'Tutorial' : type = 'Lecture hall'
break;
case 'Practical' : type = 'Lab'
break;
default : type = 'Lecture hall'
}
Rooms.find({type : type},
(err, rooms) => {
if (!err){
console.log('retrieved rooms ' + rooms)
return rooms;
}
})
}
Here I have provided only two methods because full implementation is very long and I feel if I could understand how to apply synchronous way to one method, I can manage the rest of the methods. Can someone please help me?
Well yes await is only available inside an async function so put async infront of findSuitableRoom.
Also you did a classic mistake. You use return inside of a callback function, and expect getRooms to return you some value.
async function findSuitableRoom(
_lecturer,
_sessionDay,
_sessionTime,
_sessionDuration,
_sessionType
) {
let assignedRoom = selectRoomByLevel(
preferredRooms,
lecturer.level,
_sessionDuration
);
if (!assignedRoom.success) {
try {
let rooms = await getRooms(_sessionType);
} catch (err) {
console.log("no rooms found");
}
assignedRoom = assignRoom(
_lecturer.rooms,
_sessionDuration,
_lecturer.level
);
} else {
arr_RemovedSessions.push(assignedRoom.removed);
}
return assignedRoom;
}
Also wrap it in an try / catch
Since .find() returns an promise if you dont pass an callback you can write it like this
function getRooms(type) {
switch (type) {
case "Tutorial":
type = "Lecture hall";
break;
case "Practical":
type = "Lab";
break;
default:
type = "Lecture hall";
}
return Rooms.find({ type });
}
Note here findSuitableRoom is no longer synchronouse. Its async and returns an promise. That means you will need to use the function like this:
findSuitableRoom.then(res => { console.log(res); })
The 'await' operator can only be used in an 'async' function
This means whenever you want to use the await keyword it needs to be inside a function which has an async keyword (returns promise)
read this https://javascript.info/async-await for more info
const add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => { //to make it asynchronous
if (a < 0 || b < 0) {
return reject("don't need negative");
}
resolve(a + b);
}, 2000);
});
};
const jatin = async () => {
try{
const sum = await add(10, 5);
const sum2 = await add(sum, -100);
const sum3 = await add(sum2, 1000);
return sum3;
} catch (e) {
console.log(e);
}
};
jatin()
Try this
let's understand this
add is a normal function that does some asynchronous action like
waiting for 2 seconds
normally we use await with async function so in order to use it we make as async function jatin and use await with add function call
to make it synchronous, so until first await add call() doesn't
happen it wont execute another await add call().
Example code if you will use in your app.js
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateToken();
res.status(200).send({
user,
token,
});
}
catch (error) {
console.log(error);
res.status(400).send();
}
});

Awaiting coordinates from geocoding google maps API returns pending promise

I am trying to return coordinates of given adress with gooogle maps geocoding API and fetch. I am able to log these coordinates inside my get fuction, but I have no idea how to return them from the function to use it somewhere else in the code. Already tried multiple varaitions of two approaches:
function getCoordinates1(name) {
locObj = fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${name}&key=mykey`).then( (res) => res.json()).then( (data) =>
{
console.log(data.results[0].geometry.location);
return data.results[0].geometry.location;
}).then((res) => res);
}
let coordinates1 = getCoordinates1(latinaze(name2));
console.log(coordinates1);
async function getCoordinates2(name) {
locObj = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${name}&key=mykeyk`).then( (res) => res.json()).then( (data) =>
{
console.log(data.results[0].geometry.location);
//return data.results[0].geometry.location;
}).then((res) => res);
return locObj
}
let coordinates2 = await getCoordinates2(latinaze(name2));
console.log(coordinates2);
First function returns undefined, second returns pending promise. What am I doing wrong?
The first function returns undefined because you don't return anything. As simple as that ;)
The second function returns a pending promise because you don't wait for the promise to be resolved. The promise gets resolved when the callback inside then is invoked, but that happens after you return locObj in getCoordinates2.
You should try this:
// function definition
async function getCoordinates3(name) {
const resp = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${name}&key=mykeyk`)
const data = await resp.json();
return data.results[0].geometry.location;
}
// usage
const coordinates3 = await getCoordinates3(latinaze(name3));
I couldn't return any value from function, so I made it a class method and set a property in function body. Now I can get the value form that property after I call the function:
export default class SearchModel {
constructor() {
this.start = '';
this.meta = '';
this.coors = [];
this.address = 'none';
}
//translate coordinates to address
async getAdress(coordinates) {
try {
let geocodeCoordinates = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${this.coors[0]},${this.coors[1]}&key=${process.env.API_GM_KEY}`
const rawData = await fetch(geocodeCoordinates);
//console.log(await rawData.json());
return await rawData.json();
} catch (error) {
return new Error(`Wild ERROR occured, can't get LocObj. Details: ${error}`);
} }
async displayAdress(coordinates) {
const data = await this.getAdress(coordinates);
const dataAdress = await data.results[0].formatted_address;
this.address = await dataAdress; }
}

Get back empty nodejs + firebase

Guys I need some help with a get return with node and firebase.
I get an empty array.
but in console.log inside foreach prints correct
[{cnpj: '03052164798-000179', Address: 'Av Duke of Caxias 99999', Name: 'Testing', Tel: '999999999'}]
getEmpresas() {
let empresas = []
firebase.firestore().collection('empresa').get().then(snapshot => {
snapshot.docs.forEach(empresa => {
empresas.push(empresa.data())
console.log(empresas)
});
})
return empresas
I think this is a question about async. You're not getting the data you expect in the return because that fires before the async request is made. You'd need something like:
const getEmpresas = async () => {
let empresas = []
const snapshot = await firebase
.firestore()
.collection('empresa')
.get();
snapshot.docs.forEach((empresa) => {
empresas.push(empresa.data());
});
return empresas;
};
As imjared said in his answer, the function returns before .get() gets executed because it is an asynchronous function
You will need to return the chain of firebase functions which will return a Promise
getEmpresas() {
return firebase.firestore().collection('empresa').get().then(snapshot => (
snapshot.docs.map(({ data }) => data())
))
}
Then to access the returned Promise use .then() on the Promise or await inside an async function
getEmpresas.then(empresas => console.log(empresas))
Or inside an async function
const main = async () => {
const empresas = await getEmpresas()
console.log(empresas)
}
main()

NodeJS Async / Await - Build configuration file with API call

I would like to have a configuration file with variables set with data I fetch from an API.
I think I must use async and await features to do so, otherwise my variable would stay undefined.
But I don't know how to integrate this and keep the node exports.myVariable = myData available within an async function ?
Below is the code I tried to write to do so (all in the same file) :
const fetchAPI = function(jsonQuery) {
return new Promise(function (resolve, reject) {
var reqOptions = {
headers: apiHeaders,
json:jsonQuery,
}
request.post(apiURL, function (error, res, body) {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
var wallsData = {}
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
let body = await utils.fetchAPI(jsonQuery)
let pageList = await body[0].dataHashes
for(i=0;i<pageList.length;i++){
var page = pageList[i]
wallsData[page.title.fr] = [page.difficultyList,page.wallType]
}
return wallsData
throw new Error("WOOPS")
}
try{
const wallsData = fetchWalls()
console.log(wallsData)
exports.wallsData = wallsData
}catch(err){
console.log(err)
}
The output of console.log(wallsData) shows Promise { <pending> }, therefore it is not resolved and the configuration file keep being executed without the data in wallsData...
What do I miss ?
Thanks,
Cheers
A promise is a special object that either succeeds with a result or fails with a rejection. The async-await-syntax is syntactic sugar to help to deal with promises.
If you define a function as aync it always will return a promise.
Even a function like that reads like
const foo = async() => {
return "hello";
}
returns a promise of a string, not only a string. And you need to wait until it's been resolved or rejected.
It's analogue to:
const foo = async() => {
return Promise.resolve("Hello");
}
or:
const foo = async() => {
return new Promise(resolve => resolve("Hello"));
}
Your fetchWalls similarly is a promise that will remain pending for a time. You'll have to make sure it either succeeds or fails by setting up the then or catch handlers in your outer scope:
fetchWalls()
.then(console.log)
.catch(console.error);
The outer scope is never async, so you cannot use await there. You can only use await inside other async functions.
I would also not use your try-catch for that outer scope promise handling. I think you are confusing the try-catch approach that is intended to be used within async functions, as there it helps to avoid nesting and reads like synchronous code:
E.g. you could do inside your fetchWalls defintion:
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
try {
let body = await utils.fetchAPI(jsonQuery)
} catch(e) {
// e is the reason of the promise rejection if you want to decide what to do based on it. If you would not catch it, the rejection would chain through to the first error handler.
}
...
}
Can you change the statements like,
try{
const wallsData = fetchWalls();
wallsData.then((result) => {
console.log(result);
});
exports.wallsData = wallsData; // when importing in other file this returns as promise and we should use async/await to handle this.
}catch(err){
console.log(err)
}

nodejs promisify a callback function with one parameter /callback

util.promisify appears to always expect 2 parameters from a callback function, however the existing functions do not have a seperate callback for err and data, but only a single callback.
How can I handle this ???
const {promisify} = require('util');
function y(text,cb){
setTimeout(function(){cb({error:false,data:text})},1000);
}
async function test(text) {
try{
const z = promisify(y)
return await z(text);
} catch(e) {return {error:true,msg:e}}
}
console.log(test('xxx'));
What I am looking for is to return the value from function y syncronously and not getting a promise i.e.
var x = test('xxx');
Given the information in your comment, you can wrap the function with a compatible signature to be passed directly to promisify():
const { promisify } = require('util')
function y (query, callback) {
callback(query)
}
function yCompatible (query, callback) {
y(query, ({ error, data }) => {
callback(error && data, error || data)
})
}
const yAsync = promisify(yCompatible)
async function test (query) {
try {
return yAsync(query)
} catch (error) {
return error
}
}
test('xxx').then(
data => { console.log(data) },
error => { console.error(error) }
)
Also try not to get in the habit of using single letter variables like a mathematician ;) I realize this is just example code, but even then it's helpful to be a bit more explicit about your intent.

Resources